diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index 27bb5ad..b9eacb8 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -61,16 +61,30 @@ class AssinaturaA1(Assinatura): """Classe abstrata responsavel por efetuar a assinatura do certificado digital no XML informado.""" - def assinar_arquivo(self, caminho_arquivo): + def assinar_arquivo(self, caminho_arquivo, salva=True): # Carrega o XML do arquivo raiz = etree.parse(caminho_arquivo) - return self.assinar_etree(raiz) + + # Efetua a assinatura + xml = self.assinar_etree(raiz, retorna_xml=True) + + raise Exception(xml) + + # Grava XML assinado no arquivo + if salva: + fp = file(caminho_arquivo, 'w') + fp.write(xml) + fp.close() + + return xml def assinar_xml(self, xml): raiz = etree.parse(StringIO(xml)) - return self.assinar_etree(raiz) - def assinar_etree(self, raiz): + # Efetua a assinatura + return self.assinar_etree(raiz, retorna_xml=True) + + def assinar_etree(self, raiz, retorna_xml=False): # Extrai a tag do elemento raiz tipo = extrair_tag(raiz.getroot()) @@ -91,6 +105,24 @@ class AssinaturaA1(Assinatura): URI=raiz.getroot().getchildren()[0].attrib['Id'], nsmap={'sig': NAMESPACE_SIG}, ) + + signed_info = etree.SubElement(signature, '{%s}SignedInfo'%NAMESPACE_SIG) + etree.SubElement(signed_info, 'CanonicalizationMethod', Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315") + etree.SubElement(signed_info, 'SignatureMethod', Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1") + + reference = etree.SubElement(signed_info, '{%s}Reference'%NAMESPACE_SIG, URI=raiz.getroot().getchildren()[0].attrib['Id']) + transforms = etree.SubElement(reference, 'Transforms', URI=raiz.getroot().getchildren()[0].attrib['Id']) + etree.SubElement(transforms, 'Transform', Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature") + etree.SubElement(transforms, 'Transform', Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315") + etree.SubElement(reference, '{%s}DigestMethod'%NAMESPACE_SIG, Algorithm="http://www.w3.org/2000/09/xmldsig#sha1") + digest_value = etree.SubElement(reference, '{%s}DigestValue'%NAMESPACE_SIG) + + signature_value = etree.SubElement(signature, '{%s}SignatureValue'%NAMESPACE_SIG) + + key_info = etree.SubElement(signature, '{%s}KeyInfo'%NAMESPACE_SIG) + x509_data = etree.SubElement(key_info, '{%s}X509Data'%NAMESPACE_SIG) + x509_certificate = etree.SubElement(x509_data, '{%s}X509Certificate'%NAMESPACE_SIG) + raiz.getroot().insert(0, signature) # Acrescenta a tag de doctype (como o lxml nao suporta alteracao do doctype, @@ -108,21 +140,27 @@ class AssinaturaA1(Assinatura): assinador.sign(noh_assinatura) # Coloca na instância Signature os valores calculados - doc.Signature.DigestValue = ctxt.xpathEval(u'//sig:DigestValue')[0].content.replace(u'\n', u'') - doc.Signature.SignatureValue = ctxt.xpathEval(u'//sig:SignatureValue')[0].content.replace(u'\n', u'') + digest_value.text = ctxt.xpathEval(u'//sig:DigestValue')[0].content.replace(u'\n', u'') + signature_value.text = ctxt.xpathEval(u'//sig:SignatureValue')[0].content.replace(u'\n', u'') # Provavelmente retornarão vários certificados, já que o xmlsec inclui a cadeia inteira certificados = ctxt.xpathEval(u'//sig:X509Data/sig:X509Certificate') - doc.Signature.X509Certificate = certificados[len(certificados)-1].content.replace(u'\n', u'') + x509_certificate.text = certificados[len(certificados)-1].content.replace(u'\n', u'') resultado = assinador.status == xmlsec.DSigStatusSucceeded # Limpa objetos da memoria e desativa funções criptográficas self._depois_de_assinar_ou_verificar(doc_xml, ctxt, assinador) - #print etree.tostring(raiz, pretty_print=True, xml_declaration=True, encoding='utf-8') + # Gera o XML para retornar + raise Exception(dir(doc_xml)) + xml = doc_xml.serialize() - return resultado + if retorna_xml: + raise Exception(xml) + return xml + else: + return etree.parse(StringIO(xml)) def _ativar_funcoes_criptograficas(self): # FIXME: descobrir forma de evitar o uso do libxml2 neste processo @@ -175,7 +213,6 @@ class AssinaturaA1(Assinatura): return resultado def _antes_de_assinar_ou_verificar(self, raiz): - raise Exception(dir(raiz)) # Converte etree para string xml = etree.tostring(raiz, xml_declaration=True, encoding='utf-8') diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index 27bfbec..750730d 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -3,7 +3,7 @@ from httplib import HTTPSConnection, HTTPResponse from pynfe.utils import etree, StringIO -from pynfe.utils.flags import NAMESPACE_NFE, NAMESPACE_SOAP +from pynfe.utils.flags import NAMESPACE_NFE, NAMESPACE_SOAP, VERSAO_PADRAO class Comunicacao(object): u"""Classe abstrata responsavel por definir os metodos e logica das classes @@ -21,7 +21,7 @@ class Comunicacao(object): class ComunicacaoSefaz(Comunicacao): u"""Classe de comunicação que segue o padrão definido para as SEFAZ dos Estados.""" - _versao = '1.01' + _versao = VERSAO_PADRAO def transmitir(self, nota_fiscal): pass diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index f519d4d..f36939d 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -7,7 +7,7 @@ except: from pynfe.entidades import Emitente, Cliente, Produto, Transportadora, NotaFiscal from pynfe.excecoes import NenhumObjetoEncontrado, MuitosObjetosEncontrados from pynfe.utils import etree, so_numeros, obter_municipio_por_codigo, obter_pais_por_codigo -from pynfe.utils.flags import CODIGOS_ESTADOS +from pynfe.utils.flags import CODIGOS_ESTADOS, VERSAO_PADRAO class Serializacao(object): """Classe abstrata responsavel por fornecer as funcionalidades basicas para @@ -43,6 +43,8 @@ class Serializacao(object): raise Exception('Metodo nao implementado') class SerializacaoXML(Serializacao): + _versao = VERSAO_PADRAO + def exportar(self, destino=None, retorna_string=False, **kwargs): """Gera o(s) arquivo(s) de Nofa Fiscal eletronica no padrao oficial da SEFAZ e Receita Federal, para ser(em) enviado(s) para o webservice ou para ser(em) @@ -220,7 +222,7 @@ class SerializacaoXML(Serializacao): return raiz def _serializar_notas_fiscal(self, nota_fiscal, tag_raiz='infNFe', retorna_string=True): - raiz = etree.Element(tag_raiz, versao="2.00") + raiz = etree.Element(tag_raiz, versao=self._versao) # Dados da Nota Fiscal ide = etree.SubElement(raiz, 'ide') diff --git a/pynfe/utils/flags.py b/pynfe/utils/flags.py index 77b2acb..515ec18 100644 --- a/pynfe/utils/flags.py +++ b/pynfe/utils/flags.py @@ -4,6 +4,8 @@ NAMESPACE_NFE = 'http://www.portalfiscal.inf.br/nfe' NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#' NAMESPACE_SOAP = 'http://www.w3.org/2003/05/soap-envelope' +VERSAO_PADRAO = '1.01' + TIPOS_DOCUMENTO = ( 'CNPJ', 'CPF', diff --git a/tests/03-processamento-01-serializacao-xml.txt b/tests/03-processamento-01-serializacao-xml.txt index c80a2ee..1ceb14c 100644 --- a/tests/03-processamento-01-serializacao-xml.txt +++ b/tests/03-processamento-01-serializacao-xml.txt @@ -268,7 +268,7 @@ Serializando por partes NFe52100112345678000190550010000000011518005123 >>> print serializador._serializar_notas_fiscal(nota_fiscal) - + 52 51800512