|
|
@ -1,5 +1,38 @@ |
|
|
# -*- coding: utf-8 -*- |
|
|
# -*- coding: utf-8 -*- |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
from cStringIO import StringIO |
|
|
|
|
|
except ImportError: |
|
|
|
|
|
from StringIO import StringIO |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
from lxml import etree |
|
|
|
|
|
except ImportError: |
|
|
|
|
|
try: |
|
|
|
|
|
# Python 2.5 - cElementTree |
|
|
|
|
|
import xml.etree.cElementTree as etree |
|
|
|
|
|
except ImportError: |
|
|
|
|
|
try: |
|
|
|
|
|
# Python 2.5 - ElementTree |
|
|
|
|
|
import xml.etree.ElementTree as etree |
|
|
|
|
|
except ImportError: |
|
|
|
|
|
try: |
|
|
|
|
|
# Instalacao normal do cElementTree |
|
|
|
|
|
import cElementTree as etree |
|
|
|
|
|
except ImportError: |
|
|
|
|
|
try: |
|
|
|
|
|
# Instalacao normal do ElementTree |
|
|
|
|
|
import elementtree.ElementTree as etree |
|
|
|
|
|
except ImportError: |
|
|
|
|
|
raise Exception('Falhou ao importar lxml/ElementTree') |
|
|
|
|
|
|
|
|
|
|
|
import xmlsec, libxml2 # FIXME: verificar ambiguidade de dependencias: lxml e libxml2 |
|
|
|
|
|
|
|
|
|
|
|
from geraldo.utils import memoize |
|
|
|
|
|
|
|
|
|
|
|
NAMESPACE_NFE = u'http://www.portalfiscal.inf.br/nfe' |
|
|
|
|
|
NAMESPACE_SIG = u'http://www.w3.org/2000/09/xmldsig#' |
|
|
|
|
|
|
|
|
class Assinatura(object): |
|
|
class Assinatura(object): |
|
|
"""Classe abstrata responsavel por definir os metodos e logica das classes |
|
|
"""Classe abstrata responsavel por definir os metodos e logica das classes |
|
|
de assinatura digital.""" |
|
|
de assinatura digital.""" |
|
|
@ -11,7 +44,7 @@ class Assinatura(object): |
|
|
self.certificado = certificado |
|
|
self.certificado = certificado |
|
|
self.senha = senha |
|
|
self.senha = senha |
|
|
|
|
|
|
|
|
def assinar_arquivos(self, caminho_raiz): |
|
|
|
|
|
|
|
|
def assinar_arquivo(self, caminho_arquivo): |
|
|
"""Efetua a assinatura dos arquivos XML informados""" |
|
|
"""Efetua a assinatura dos arquivos XML informados""" |
|
|
pass |
|
|
pass |
|
|
|
|
|
|
|
|
@ -33,7 +66,7 @@ class Assinatura(object): |
|
|
"""Efetua a assinatura em instancias do PyNFe""" |
|
|
"""Efetua a assinatura em instancias do PyNFe""" |
|
|
pass |
|
|
pass |
|
|
|
|
|
|
|
|
def verificar_arquivos(self, caminho_raiz): |
|
|
|
|
|
|
|
|
def verificar_arquivo(self, caminho_arquivo): |
|
|
pass |
|
|
pass |
|
|
|
|
|
|
|
|
def verificar_xml(self, xml): |
|
|
def verificar_xml(self, xml): |
|
|
@ -45,9 +78,112 @@ class Assinatura(object): |
|
|
def verificar_objetos(self, objetos): |
|
|
def verificar_objetos(self, objetos): |
|
|
pass |
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@memoize |
|
|
|
|
|
def extrair_tag(root): |
|
|
|
|
|
return root.tag.split('}')[-1] |
|
|
|
|
|
|
|
|
class AssinaturaA1(Assinatura): |
|
|
class AssinaturaA1(Assinatura): |
|
|
"""Classe abstrata responsavel por efetuar a assinatura do certificado |
|
|
"""Classe abstrata responsavel por efetuar a assinatura do certificado |
|
|
digital no XML informado.""" |
|
|
digital no XML informado.""" |
|
|
|
|
|
|
|
|
pass |
|
|
|
|
|
|
|
|
def assinar_arquivo(self, caminho_arquivo): |
|
|
|
|
|
# Carrega o XML do arquivo |
|
|
|
|
|
raiz = etree.parse(caminho_arquivo) |
|
|
|
|
|
return self.assinar_etree(raiz) |
|
|
|
|
|
|
|
|
|
|
|
def assinar_xml(self, xml): |
|
|
|
|
|
raiz = etree.parse(StringIO(xml)) |
|
|
|
|
|
return self.assinar_etree(raiz) |
|
|
|
|
|
|
|
|
|
|
|
def assinar_etree(self, raiz): |
|
|
|
|
|
# Extrai a tag do elemento raiz |
|
|
|
|
|
tipo = extrair_tag(raiz.getroot()) |
|
|
|
|
|
|
|
|
|
|
|
# doctype compatível com o tipo da tag raiz |
|
|
|
|
|
if tipo == u'NFe': |
|
|
|
|
|
doctype = u'<!DOCTYPE NFe [<!ATTLIST infNFe Id ID #IMPLIED>]>' |
|
|
|
|
|
elif tipo == u'inutNFe': |
|
|
|
|
|
doctype = u'<!DOCTYPE inutNFe [<!ATTLIST infInut Id ID #IMPLIED>]>' |
|
|
|
|
|
elif tipo == u'cancNFe': |
|
|
|
|
|
doctype = u'<!DOCTYPE cancNFe [<!ATTLIST infCanc Id ID #IMPLIED>]>' |
|
|
|
|
|
elif tipo == u'DPEC': |
|
|
|
|
|
doctype = u'<!DOCTYPE DPEC [<!ATTLIST infDPEC Id ID #IMPLIED>]>' |
|
|
|
|
|
|
|
|
|
|
|
# Tag de assinatura |
|
|
|
|
|
if raiz.getroot().find('Signature') is None: |
|
|
|
|
|
signature = etree.Element( |
|
|
|
|
|
'Signature', |
|
|
|
|
|
URI=raiz.getroot().getchildren()[0].attrib['Id'], |
|
|
|
|
|
xmlns=NAMESPACE_SIG, |
|
|
|
|
|
) |
|
|
|
|
|
signature.text = '' |
|
|
|
|
|
raiz.getroot().insert(0, signature) |
|
|
|
|
|
|
|
|
|
|
|
# Acrescenta a tag de doctype (como o lxml nao suporta alteracao do doctype, |
|
|
|
|
|
# converte para string para faze-lo) |
|
|
|
|
|
xml = etree.tostring(raiz, xml_declaration=True, encoding='utf-8') |
|
|
|
|
|
pos = xml.find('>') + 1 |
|
|
|
|
|
xml = xml[:pos] + doctype + xml[pos:] |
|
|
|
|
|
raiz = etree.parse(StringIO(xml)) |
|
|
|
|
|
|
|
|
|
|
|
# Ativa funções criptográficas |
|
|
|
|
|
self._ativa_funcoes_criptograficas() |
|
|
|
|
|
|
|
|
|
|
|
# Colocamos o texto no avaliador XML |
|
|
|
|
|
#doc_xml = libxml2.parseMemory(xml, len(xml)) |
|
|
|
|
|
|
|
|
|
|
|
# Cria o contexto para manipulação do XML via sintaxe XPATH |
|
|
|
|
|
#ctxt = doc_xml.xpathNewContext() |
|
|
|
|
|
#ctxt.xpathRegisterNs(u'sig', NAMESPACE_SIG) |
|
|
|
|
|
|
|
|
|
|
|
# Separa o nó da assinatura |
|
|
|
|
|
#noh_assinatura = ctxt.xpathEval(u'//*/sig:Signature')[0] |
|
|
|
|
|
|
|
|
|
|
|
# Buscamos a chave no arquivo do certificado |
|
|
|
|
|
chave = xmlsec.cryptoAppKeyLoad( |
|
|
|
|
|
filename=str(self.certificado.caminho_arquivo), |
|
|
|
|
|
format=xmlsec.KeyDataFormatPkcs12, |
|
|
|
|
|
pwd=str(self.senha), |
|
|
|
|
|
pwdCallback=None, |
|
|
|
|
|
pwdCallbackCtx=None, |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
# Cria a variável de chamada (callable) da função de assinatura |
|
|
|
|
|
assinador = xmlsec.DSigCtx() |
|
|
|
|
|
|
|
|
|
|
|
# Atribui a chave ao assinador |
|
|
|
|
|
assinador.signKey = chave |
|
|
|
|
|
|
|
|
|
|
|
# Desativa funções criptográficas |
|
|
|
|
|
self._desativa_funcoes_criptograficas() |
|
|
|
|
|
|
|
|
|
|
|
#print etree.tostring(raiz, pretty_print=True, xml_declaration=True, encoding='utf-8') |
|
|
|
|
|
|
|
|
|
|
|
def _ativa_funcoes_criptograficas(self): |
|
|
|
|
|
# Ativa as funções de análise de arquivos XML FIXME |
|
|
|
|
|
libxml2.initParser() |
|
|
|
|
|
libxml2.substituteEntitiesDefault(1) |
|
|
|
|
|
|
|
|
|
|
|
# Ativa as funções da API de criptografia |
|
|
|
|
|
xmlsec.init() |
|
|
|
|
|
xmlsec.cryptoAppInit(None) |
|
|
|
|
|
xmlsec.cryptoInit() |
|
|
|
|
|
|
|
|
|
|
|
def _desativa_funcoes_criptograficas(self): |
|
|
|
|
|
''' Desativa as funções criptográficas e de análise XML |
|
|
|
|
|
As funções devem ser chamadas aproximadamente na ordem inversa da ativação |
|
|
|
|
|
''' |
|
|
|
|
|
|
|
|
|
|
|
# Shutdown xmlsec-crypto library |
|
|
|
|
|
xmlsec.cryptoShutdown() |
|
|
|
|
|
|
|
|
|
|
|
# Shutdown crypto library |
|
|
|
|
|
xmlsec.cryptoAppShutdown() |
|
|
|
|
|
|
|
|
|
|
|
# Shutdown xmlsec library |
|
|
|
|
|
xmlsec.shutdown() |
|
|
|
|
|
|
|
|
|
|
|
# Shutdown LibXML2 FIXME |
|
|
|
|
|
libxml2.cleanupParser() |
|
|
|
|
|
|