Created
December 22, 2020 19:18
-
-
Save rodrigorodriguescosta/b6801af6358c818689f48b90d4134101 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import falcon | |
import signxml | |
from OpenSSL import crypto | |
from lxml import etree | |
from signxml import XMLSigner | |
from wsgiref.simple_server import make_server | |
def find_between( s, first, last ): | |
try: | |
start = s.index( first ) | |
end = s.index( last, start ) + len( last ) | |
return s[start:end] | |
except ValueError: | |
return "" | |
def assina_xml(cert, key, xml_string): | |
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') | |
# if reference: | |
# reference = '#%s' % reference | |
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True, recover=True) | |
xml_element = etree.fromstring(xml_string.encode(), parser=parser) | |
reference = '#'+ xml_element.xpath('//@Id')[0] | |
ns = {} | |
ns[None] = signer.namespaces['ds'] | |
signer.namespaces = ns | |
signed_root = signer.sign( | |
xml_element, | |
key=key, | |
cert=cert, | |
reference_uri=reference) | |
assinatura = etree.tostring(signed_root).decode() | |
assinatura = find_between(assinatura, '<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">', '</Signature>') | |
return assinatura | |
def extract_cert_and_key_from_pfx(arquivo_pfx, password): | |
pfx = open(arquivo_pfx, 'rb').read() | |
try: | |
pfx = crypto.load_pkcs12(pfx, password) | |
except Exception as e: | |
if 'mac verify failure' in str(e): | |
raise Exception('Senha do certificado inválida') | |
# PEM formatted private key | |
key = crypto.dump_privatekey(crypto.FILETYPE_PEM, | |
pfx.get_privatekey()) | |
# PEM formatted certificate | |
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, | |
pfx.get_certificate()) | |
return cert, key | |
def test(): | |
cert, key = extract_cert_and_key_from_pfx('meu_certificado.pfx', '123456') | |
# xml = '<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="4.00"><idLote>1500</idLote><indSinc>0</indSinc><NFe xmlns="http://www.portalfiscal.inf.br/nfe"><infNFe Id="NFe24201019843657000149650010000086901016899378" versao="4.00"><ide><cUF>24</cUF><cNF>01689937</cNF><natOp>VENDA</natOp><mod>65</mod><serie>1</serie><nNF>8690</nNF><dhEmi>2020-10-23T19:28:49-03:00</dhEmi><tpNF>1</tpNF><idDest>1</idDest><cMunFG>2408003</cMunFG><tpImp>4</tpImp><tpEmis>1</tpEmis><cDV>8</cDV><tpAmb>1</tpAmb><finNFe>1</finNFe><indFinal>1</indFinal><indPres>1</indPres><procEmi>0</procEmi><verProc>Sigeflex ERP</verProc></ide><emit><CNPJ>19843657000149</CNPJ><xNome>CASA DO CONSTRUTOR MATERIAIS DE CONSTRUCAO LTDA</xNome><xFant>CASA DO CONSTRUTOR</xFant><enderEmit><xLgr>AVENIDA WILSON ROSADO</xLgr><nro>15</nro><xBairro>ALTO DO SUMARE</xBairro><cMun>2408003</cMun><xMun>MOSSORO</xMun><UF>RN</UF><CEP>59633730</CEP><cPais>1058</cPais><xPais>BRASIL</xPais><fone>8433127000</fone></enderEmit><IE>204051975</IE><CRT>1</CRT></emit><det nItem="1"><prod><cProd>0491.00089.0010PCR</cProd><cEAN>7894174211422</cEAN><xProd>FITA VEDA TUDO QUARTZOLIT 10CM - PC</xProd><NCM>68071000</NCM><CFOP>5102</CFOP><uCom>UN</uCom><qCom>1.0000</qCom><vUnCom>6.000000</vUnCom><vProd>6.00</vProd><cEANTrib>7894174211422</cEANTrib><uTrib>UN</uTrib><qTrib>1.0000</qTrib><vUnTrib>6.00000</vUnTrib><indTot>1</indTot></prod><imposto><vTotTrib>2.00</vTotTrib><ICMS><ICMSSN102><orig>0</orig><CSOSN>102</CSOSN></ICMSSN102></ICMS><PIS><PISAliq><CST>01</CST><vBC>6.00</vBC><pPIS>1.65</pPIS><vPIS>0.10</vPIS></PISAliq></PIS><COFINS><COFINSOutr><CST>99</CST><qBCProd>0</qBCProd><vAliqProd>7.60</vAliqProd><vCOFINS>0.46</vCOFINS></COFINSOutr></COFINS></imposto></det><total><ICMSTot><vBC>0.00</vBC><vICMS>0.00</vICMS><vICMSDeson>0.00</vICMSDeson><vFCP>0.00</vFCP><vBCST>0.00</vBCST><vST>0.00</vST><vFCPST>0.00</vFCPST><vFCPSTRet>0.00</vFCPSTRet><vProd>6.00</vProd><vFrete>0.00</vFrete><vSeg>0.00</vSeg><vDesc>0.00</vDesc><vII>0.00</vII><vIPI>0.00</vIPI><vIPIDevol>0.00</vIPIDevol><vPIS>0.10</vPIS><vCOFINS>0.46</vCOFINS><vOutro>0.00</vOutro><vNF>6.00</vNF><vTotTrib>2.00</vTotTrib></ICMSTot></total><transp><modFrete>9</modFrete></transp><pag><detPag><tPag>01</tPag><vPag>6.00</vPag></detPag></pag></infNFe></NFe></enviNFe>' | |
xml = '<test><subelement aaaa="123"></subelement></test>' | |
assinatura = assina_xml(cert, key, xml) | |
xml = xml % (infNfe + assinatura) | |
print(xml) | |
class NfeResource(object): | |
def on_post(self, req, resp): | |
cert = req.get_param("cert", required=True) | |
key = req.get_param("key", required=True) | |
xml = req.get_param("xml", required=True) | |
xml_assinado = assina_xml(cert, key, xml) | |
resp.media = xml_assinado | |
app = falcon.API() | |
app.req_options.auto_parse_form_urlencoded=True | |
nfe = NfeResource() | |
app.add_route('/assina_xml', nfe) | |
api = application = falcon.API() | |
if __name__ == '__main__': | |
with make_server('', 8181, app) as httpd: | |
print('Serving on port 8181...') | |
# Serve until process is killed | |
httpd.serve_forever() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment