Browse Source

Nova forma de assinatura usando o signXML

tags/0.3.10
Leonardo 9 years ago
parent
commit
264ea14bed
  1. 17
      pynfe/entidades/certificado.py
  2. 50
      pynfe/processamento/assinatura.py
  3. 7
      pynfe/utils/__init__.py

17
pynfe/entidades/certificado.py

@ -9,7 +9,7 @@ import os
class Certificado(Entidade): class Certificado(Entidade):
"""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. classes de certificados digitais.
Caso va implementar um novo formato de certificado, crie uma classe que Caso va implementar um novo formato de certificado, crie uma classe que
herde desta.""" herde desta."""
@ -29,15 +29,15 @@ class CertificadoA1(Certificado):
def __init__(self, caminho_arquivo=None): def __init__(self, caminho_arquivo=None):
self.caminho_arquivo = caminho_arquivo self.caminho_arquivo = caminho_arquivo
self.arquivos_temp = [] self.arquivos_temp = []
def separar_arquivo(self, senha, caminho=False): def separar_arquivo(self, senha, caminho=False):
"""Separa o arquivo de certificado em dois: de chave e de certificado, """Separa o arquivo de certificado em dois: de chave e de certificado,
e retorna a string. Se caminho for True grava na pasta temporaria e retorna e retorna a string. Se caminho for True grava na pasta temporaria e retorna
o caminho dos arquivos, apos o uso devem ser excluidos com o metodo excluir."""
o caminho dos arquivos, senao retorna o objeto. Apos o uso devem ser excluidos com o metodo excluir."""
# Carrega o arquivo .pfx, erro pode ocorrer se a senha estiver errada ou formato invalido. # 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) pkcs12 = crypto.load_pkcs12(open(self.caminho_arquivo, "rb").read(), senha)
if caminho: if caminho:
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, pkcs12.get_certificate()) cert = crypto.dump_certificate(crypto.FILETYPE_PEM, pkcs12.get_certificate())
chave = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey()) chave = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey())
@ -55,12 +55,12 @@ class CertificadoA1(Certificado):
cert = cert.replace('\n', '') cert = cert.replace('\n', '')
cert = cert.replace('-----BEGIN CERTIFICATE-----', '') cert = cert.replace('-----BEGIN CERTIFICATE-----', '')
cert = cert.replace('-----END CERTIFICATE-----', '') cert = cert.replace('-----END CERTIFICATE-----', '')
# Chave, string decodificada da chave privada # Chave, string decodificada da chave privada
chave = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey()) chave = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey())
return chave, cert return chave, cert
def excluir(self): def excluir(self):
"""Exclui os arquivos temporarios utilizados para o request.""" """Exclui os arquivos temporarios utilizados para o request."""
try: try:
@ -69,4 +69,3 @@ class CertificadoA1(Certificado):
self.arquivos_temp.clear() self.arquivos_temp.clear()
except: except:
pass pass

50
pynfe/processamento/assinatura.py

@ -1,7 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from pynfe.utils import etree, remover_acentos from pynfe.utils import etree, remover_acentos
from pynfe.utils.flags import NAMESPACE_SIG
import subprocess import subprocess
import signxml
from signxml import XMLSigner
from pynfe.entidades import CertificadoA1
class Assinatura(object): class Assinatura(object):
@ -323,3 +327,49 @@ class AssinaturaA1(Assinatura):
return xml return xml
except Exception as e: except Exception as e:
raise e raise e
class AssinaturaA1SignXML(Assinatura):
def __init__(self, certificado, senha):
self.key, self.cert = CertificadoA1(certificado).separar_arquivo(senha)
def assinar(self, xml, retorna_string=False):
if len(xml.nsmap.items()) == 0: # não tem namespace
reference = xml.find('infNFe').attrib['Id']
else:
ns = {'ns': 'http://www.portalfiscal.inf.br/nfe'}
reference = xml.find('ns:infNFe', namespaces=ns).attrib['Id']
# retira acentos
xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False))
xml = etree.fromstring(xml_str)
signer = XMLSigner(
method=signxml.methods.enveloped, signature_algorithm="rsa-sha1",
digest_algorithm='sha1',
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
ns = {None: signer.namespaces['ds']}
signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
signed_root = signer.sign(
xml, key=self.key, cert=self.cert, reference_uri=ref_uri)
ns = {'ns': NAMESPACE_SIG}
if reference:
element_signed = signed_root.find(".//*[@Id='%s']" % reference)
signature = signed_root.find(".//ns:Signature", namespaces=ns)
if element_signed is not None and signature is not None:
parent = element_signed.getparent()
parent.append(signature)
# coloca o certificado na tag X509Data/X509Certificate
tagX509Data = signed_root.find('.//ns:X509Data', namespaces=ns)
etree.SubElement(tagX509Data, 'X509Certificate').text = self.cert
if retorna_string:
return etree.tostring(signed_root, encoding="unicode", pretty_print=False)
else:
return signed_root

7
pynfe/utils/__init__.py

@ -9,10 +9,7 @@ try:
except ImportError: except ImportError:
raise Exception('Falhou ao importar lxml/ElementTree') raise Exception('Falhou ao importar lxml/ElementTree')
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from io import StringIO
try: try:
from . import flags from . import flags
@ -148,4 +145,4 @@ def obter_uf_por_codigo(codigo_uf):
def remover_acentos(txt): def remover_acentos(txt):
return normalize('NFKD', txt).encode('ASCII','ignore').decode('ASCII')
return normalize('NFKD', txt).encode('ASCII','ignore').decode('ASCII')
Loading…
Cancel
Save