Browse Source

Merge branch 'master' of https://github.com/leotada/PyNFe

tags/0.3.10
Junior Tada 9 years ago
parent
commit
1817487d26
  1. 17
      pynfe/entidades/certificado.py
  2. 39
      pynfe/processamento/assinatura.py
  3. 14
      pynfe/processamento/comunicacao.py
  4. 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

39
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,38 @@ 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):
# 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

14
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, \ from pynfe.utils.flags import NAMESPACE_NFE, NAMESPACE_SOAP, NAMESPACE_XSI, NAMESPACE_XSD, NAMESPACE_METODO, \
VERSAO_PADRAO, CODIGOS_ESTADOS, NAMESPACE_BETHA VERSAO_PADRAO, CODIGOS_ESTADOS, NAMESPACE_BETHA
from pynfe.utils.webservices import NFCE, NFE, NFSE from pynfe.utils.webservices import NFCE, NFE, NFSE
from .assinatura import AssinaturaA1
from .assinatura import AssinaturaA1, AssinaturaA1SignXML
from pynfe.entidades.certificado import CertificadoA1 from pynfe.entidades.certificado import CertificadoA1
@ -205,7 +205,7 @@ class ComunicacaoSefaz(Comunicacao):
return self._post(url, xml) return self._post(url, xml)
def download(self, cnpj, chave): def download(self, cnpj, chave):
"""
"""
Metodo para download de NFe por parte de destinatário. Metodo para download de NFe por parte de destinatário.
O certificado digital deve ser o mesmo do destinatário da Nfe. O certificado digital deve ser o mesmo do destinatário da Nfe.
NT 2012/002 NT 2012/002
@ -221,7 +221,7 @@ class ComunicacaoSefaz(Comunicacao):
# Monta XML para envio da requisição # Monta XML para envio da requisição
xml = self._construir_xml_status_pr(cabecalho=self._cabecalho_soap(metodo='NfeDownloadNF'), metodo='NfeDownloadNF', dados=raiz) xml = self._construir_xml_status_pr(cabecalho=self._cabecalho_soap(metodo='NfeDownloadNF'), metodo='NfeDownloadNF', dados=raiz)
return self._post(url, xml) return self._post(url, xml)
def inutilizacao(self, modelo, cnpj, numero_inicial, numero_final, justificativa='', ano=None, serie='1'): 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 etree.SubElement(inf_inut, 'xJust').text = justificativa
# assinatura # assinatura
a1 = AssinaturaA1(self.certificado, self.certificado_senha)
a1 = AssinaturaA1SignXML(self.certificado, self.certificado_senha)
xml = a1.assinar(raiz) xml = a1.assinar(raiz)
# Monta XML para envio da requisição # Monta XML para envio da requisição
@ -368,7 +368,7 @@ class ComunicacaoSefaz(Comunicacao):
etree.SubElement(raiz, 'versaoDados').text = '1.01' etree.SubElement(raiz, 'versaoDados').text = '1.01'
elif metodo == 'NfeDownloadNF': elif metodo == 'NfeDownloadNF':
etree.SubElement(raiz, 'versaoDados').text = '1.00' etree.SubElement(raiz, 'versaoDados').text = '1.00'
elif metodo == 'CadConsultaCadastro2':
elif metodo == 'CadConsultaCadastro2':
etree.SubElement(raiz, 'versaoDados').text = '2.00' etree.SubElement(raiz, 'versaoDados').text = '2.00'
else: else:
etree.SubElement(raiz, 'versaoDados').text = VERSAO_PADRAO etree.SubElement(raiz, 'versaoDados').text = VERSAO_PADRAO
@ -412,7 +412,7 @@ class ComunicacaoSefaz(Comunicacao):
try: try:
xml_declaration='<?xml version="1.0" encoding="utf-8"?>' xml_declaration='<?xml version="1.0" encoding="utf-8"?>'
# limpa xml com caracteres bugados para infNFeSupl em NFC-e # limpa xml com caracteres bugados para infNFeSupl em NFC-e
xml = re.sub('<qrCode>(.*?)</qrCode>',
xml = re.sub('<qrCode>(.*?)</qrCode>',
lambda x:x.group(0).replace('&lt;','<').replace('&gt;','>').replace('amp;',''), lambda x:x.group(0).replace('&lt;','<').replace('&gt;','>').replace('amp;',''),
etree.tostring(xml, encoding='unicode').replace('\n','')) etree.tostring(xml, encoding='unicode').replace('\n',''))
xml = xml_declaration + xml xml = xml_declaration + xml
@ -530,7 +530,7 @@ class ComunicacaoNfse(Comunicacao):
return self._post(url, xml, 'cancelar') return self._post(url, xml, 'cancelar')
# Ginfes # Ginfes
elif self.autorizador == 'GINFES': elif self.autorizador == 'GINFES':
# comunica via wsdl com certificado
# comunica via wsdl com certificado
return self._post_https(url, xml, 'cancelar') return self._post_https(url, xml, 'cancelar')
# TODO outros autorizadres # TODO outros autorizadres
else: else:

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