diff --git a/pynfe/entidades/certificado.py b/pynfe/entidades/certificado.py
index 461bd3b..61eac5d 100644
--- a/pynfe/entidades/certificado.py
+++ b/pynfe/entidades/certificado.py
@@ -9,7 +9,7 @@ import os
class Certificado(Entidade):
"""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
herde desta."""
@@ -29,15 +29,15 @@ class CertificadoA1(Certificado):
def __init__(self, caminho_arquivo=None):
self.caminho_arquivo = caminho_arquivo
self.arquivos_temp = []
-
+
def separar_arquivo(self, senha, caminho=False):
"""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
- 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.
pkcs12 = crypto.load_pkcs12(open(self.caminho_arquivo, "rb").read(), senha)
-
+
if caminho:
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, pkcs12.get_certificate())
chave = crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs12.get_privatekey())
@@ -55,12 +55,12 @@ class CertificadoA1(Certificado):
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
-
+
def excluir(self):
"""Exclui os arquivos temporarios utilizados para o request."""
try:
@@ -69,4 +69,3 @@ class CertificadoA1(Certificado):
self.arquivos_temp.clear()
except:
pass
-
diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py
index 2e0472e..a2f74a5 100644
--- a/pynfe/processamento/assinatura.py
+++ b/pynfe/processamento/assinatura.py
@@ -1,7 +1,11 @@
# -*- coding: utf-8 -*-
from pynfe.utils import etree, remover_acentos
+from pynfe.utils.flags import NAMESPACE_SIG
import subprocess
+import signxml
+from signxml import XMLSigner
+from pynfe.entidades import CertificadoA1
class Assinatura(object):
@@ -323,3 +327,38 @@ class AssinaturaA1(Assinatura):
return xml
except Exception as 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):
+ # busca tag que tem id(reference_uri), logo nao importa se tem namespace
+ reference = xml.find(".//*[@Id]").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}
+ # 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
diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py
index 1472788..0d1891f 100644
--- a/pynfe/processamento/comunicacao.py
+++ b/pynfe/processamento/comunicacao.py
@@ -6,7 +6,7 @@ from pynfe.utils import etree, so_numeros
from pynfe.utils.flags import NAMESPACE_NFE, NAMESPACE_SOAP, NAMESPACE_XSI, NAMESPACE_XSD, NAMESPACE_METODO, \
VERSAO_PADRAO, CODIGOS_ESTADOS, NAMESPACE_BETHA
from pynfe.utils.webservices import NFCE, NFE, NFSE
-from .assinatura import AssinaturaA1
+from .assinatura import AssinaturaA1, AssinaturaA1SignXML
from pynfe.entidades.certificado import CertificadoA1
@@ -205,7 +205,7 @@ class ComunicacaoSefaz(Comunicacao):
return self._post(url, xml)
def download(self, cnpj, chave):
- """
+ """
Metodo para download de NFe por parte de destinatário.
O certificado digital deve ser o mesmo do destinatário da Nfe.
NT 2012/002
@@ -221,7 +221,7 @@ class ComunicacaoSefaz(Comunicacao):
# Monta XML para envio da requisição
xml = self._construir_xml_status_pr(cabecalho=self._cabecalho_soap(metodo='NfeDownloadNF'), metodo='NfeDownloadNF', dados=raiz)
-
+
return self._post(url, xml)
def inutilizacao(self, modelo, cnpj, numero_inicial, numero_final, justificativa='', ano=None, serie='1'):
@@ -260,7 +260,7 @@ class ComunicacaoSefaz(Comunicacao):
etree.SubElement(inf_inut, 'xJust').text = justificativa
# assinatura
- a1 = AssinaturaA1(self.certificado, self.certificado_senha)
+ a1 = AssinaturaA1SignXML(self.certificado, self.certificado_senha)
xml = a1.assinar(raiz)
# Monta XML para envio da requisição
@@ -368,7 +368,7 @@ class ComunicacaoSefaz(Comunicacao):
etree.SubElement(raiz, 'versaoDados').text = '1.01'
elif metodo == 'NfeDownloadNF':
etree.SubElement(raiz, 'versaoDados').text = '1.00'
- elif metodo == 'CadConsultaCadastro2':
+ elif metodo == 'CadConsultaCadastro2':
etree.SubElement(raiz, 'versaoDados').text = '2.00'
else:
etree.SubElement(raiz, 'versaoDados').text = VERSAO_PADRAO
@@ -412,7 +412,7 @@ class ComunicacaoSefaz(Comunicacao):
try:
xml_declaration=''
# limpa xml com caracteres bugados para infNFeSupl em NFC-e
- xml = re.sub('(.*?)',
+ xml = re.sub('(.*?)',
lambda x:x.group(0).replace('<','<').replace('>','>').replace('amp;',''),
etree.tostring(xml, encoding='unicode').replace('\n',''))
xml = xml_declaration + xml
@@ -530,7 +530,7 @@ class ComunicacaoNfse(Comunicacao):
return self._post(url, xml, 'cancelar')
# Ginfes
elif self.autorizador == 'GINFES':
- # comunica via wsdl com certificado
+ # comunica via wsdl com certificado
return self._post_https(url, xml, 'cancelar')
# TODO outros autorizadres
else:
diff --git a/pynfe/utils/__init__.py b/pynfe/utils/__init__.py
index 7259a5c..2aeeab6 100644
--- a/pynfe/utils/__init__.py
+++ b/pynfe/utils/__init__.py
@@ -9,10 +9,7 @@ try:
except ImportError:
raise Exception('Falhou ao importar lxml/ElementTree')
-try:
- from StringIO import StringIO
-except ImportError:
- from io import StringIO
+from io import StringIO
try:
from . import flags
@@ -148,4 +145,4 @@ def obter_uf_por_codigo(codigo_uf):
def remover_acentos(txt):
- return normalize('NFKD', txt).encode('ASCII','ignore').decode('ASCII')
\ No newline at end of file
+ return normalize('NFKD', txt).encode('ASCII','ignore').decode('ASCII')