diff --git a/pynfe/entidades/certificado.py b/pynfe/entidades/certificado.py index 370f636..ddb24b2 100644 --- a/pynfe/entidades/certificado.py +++ b/pynfe/entidades/certificado.py @@ -1,12 +1,11 @@ # -*- coding: utf-8 -*- -import os from .base import Entidade - from OpenSSL import crypto + class Certificado(Entidade): - u"""Classe abstrata responsavel por definir o modelo padrao para as demais + """Classe abstrata responsavel por definir o modelo padrao para as demais classes de certificados digitais. Caso va implementar um novo formato de certificado, crie uma classe que @@ -18,41 +17,31 @@ class Certificado(Entidade): else: return super(Certificado, cls).__new__(cls) + class CertificadoA1(Certificado): - u"""Implementa a entidade do certificado eCNPJ A1, suportado pelo OpenSSL, + """Implementa a entidade do certificado eCNPJ A1, suportado pelo OpenSSL, e amplamente utilizado.""" caminho_arquivo = None - conteudo_x509 = None - pasta_temporaria = '/tmp/' - arquivo_chave = 'key.pem' - arquivo_cert = 'cert.pem' - - def __init__(self, caminho_arquivo=None, conteudo_x509=None): - self.caminho_arquivo = caminho_arquivo or self.caminho_arquivo - self.conteudo_x509 = conteudo_x509 or self.conteudo_x509 + + def __init__(self, caminho_arquivo=None): + self.caminho_arquivo = caminho_arquivo def separar_arquivo(self, senha, caminho_chave=None, caminho_cert=None): - u"""Separa o arquivo de certificado em dois: de chave e de certificado, - em arquivos temporários separados""" + """Separa o arquivo de certificado em dois: de chave e de certificado, + e retorna a string.""" - caminho_chave = caminho_chave or os.path.join(self.pasta_temporaria, self.arquivo_chave) - caminho_cert = caminho_cert or os.path.join(self.pasta_temporaria, self.arquivo_cert) - - # Lendo o arquivo pfx no formato pkcs12 como binario - pkcs12 = crypto.load_pkcs12(open(self.caminho_arquivo, 'rb').read(), senha) - - # Retorna a string decodificado da chave privada - key_str = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey()) - - # Retorna a string decodificado do certificado - cert_str = crypto.dump_certificate(crypto.FILETYPE_PEM, pkcs12.get_certificate()) - - # Gravando a string no dicso - open(caminho_cert, 'wb').write(cert_str) - - # Gravando a string no dicso - open(caminho_chave, 'wb').write(key_str) - - return caminho_chave, caminho_cert + # Carrega o arquivo .pfx, erro pode ocorrer se a senha estiver errada ou formato invalido. + pkcs12 = crypto.load_pkcs12(open(self.caminho_arquivo, "rb").read(), senha) + + # Certificado + cert = crypto.dump_certificate(crypto.FILETYPE_PEM, pkcs12.get_certificate()).decode('utf-8') + cert = cert.replace('\n', '') + cert = cert.replace('-----BEGIN CERTIFICATE-----', '') + cert = cert.replace('-----END CERTIFICATE-----', '') + + # Chave, string decodificada da chave privada + chave = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey()) + + return chave, cert diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index b14d133..0165448 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import signxml +from OpenSSL import crypto from pynfe.utils import etree from pynfe.entidades.certificado import CertificadoA1 from pynfe.utils.flags import NAMESPACE_NFE, NAMESPACE_SIG @@ -27,22 +28,17 @@ class AssinaturaA1(Assinatura): def assinar(self, xml): arquivo_cert = CertificadoA1(self.certificado) - #key, cert = arquivo_cert.separar_arquivo(self.senha) - cert = open("cert.pem").read() - key = open("key.pem", "rb").read() + chave, cert = arquivo_cert.separar_arquivo(self.senha) - begin = cert.find('-----BEGIN CERTIFICATE-----') - if begin < 0: - raise Exception('Formato de certificado inválido. Não encontrado tag inicial BEGIN.') - cert = cert[begin:] - - # converte xml para bytes antes do parse #root = etree.parse(xml).getroot() # caminho root = etree.fromstring(xml) # string signer = signxml.xmldsig(root, digest_algorithm="sha1") - signer.sign(method=signxml.methods.enveloped, key=key, cert=cert, + signer.sign(method=signxml.methods.enveloped, key=chave, cert=cert, algorithm="rsa-sha1", c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315', - reference_uri='#NFe41150715380524000122651010000000271333611649') + reference_uri='') #verified_data = signer.verify(require_x509=True, ca_pem_file="cert.pem") + + #root = etree.SubElement(signer.data, "{http://www.w3.org/2000/09/xmldsig#}Reference", + # URI='#NFe41150715389524000122651010000000271333611649') result = etree.tostring(signer.data) return result diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index c3dc580..4046092 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -58,7 +58,7 @@ class SerializacaoXML(Serializacao): raiz.append(self._serializar_nota_fiscal(nf, retorna_string=False)) if retorna_string: - return etree.tostring(raiz, pretty_print=True) + return etree.tostring(raiz, pretty_print=True).decode('utf-8') else: return raiz diff --git a/test.py b/test.py index 49a859a..6c906cd 100644 --- a/test.py +++ b/test.py @@ -57,7 +57,7 @@ nota_fiscal = NotaFiscal( cliente=cliente, uf='PR', codigo_numerico_aleatorio='66998237', - natureza_operacao='VENDA ASSINATURAS', + natureza_operacao='VENDA', forma_pagamento='1', modelo=65, serie='1', @@ -100,9 +100,9 @@ nota_fiscal.adicionar_produto_servico(codigo='000328', # id do produto (000328 e cofins_valor=Decimal('3.51')) serializador = SerializacaoXML(_fonte_dados, homologacao=True) -xml = serializador.exportar(retorna_string=True).decode('utf-8') -certificado = "certificado_A1.pfx" -senha = 'sua_senha' +xml = serializador.exportar(retorna_string=True) +certificado = "JC.pfx" +senha = '12345678' # assinatura a1 = AssinaturaA1(certificado, senha) xml = a1.assinar_nfe(xml)