From 153d577100d4044de2c80381334cab2eecd15f2d Mon Sep 17 00:00:00 2001 From: "bit4bit@riseup.net" Date: Sun, 6 Sep 2020 16:24:10 +0000 Subject: [PATCH] facho: clip.py se adiciona nuevo comand 'sign-xml'. * facho/cli.py: adiciona nuevo comando 'sign-xml' para firmar directamente un xml. * facho/fe/fe.py (DianXMLExtensionSigner.sign_xml_string): Nuevo metodo. FossilOrigin-Name: 61920c40da14a134de6392845b3e4d98ad2b1b683093038d6161c147669127e9 --- facho/cli.py | 39 ++++++++++++++++++++++++++------------- facho/facho.py | 1 + facho/fe/fe.py | 44 +++++++++++++++----------------------------- tests/test_fe.py | 10 ++++++++++ 4 files changed, 52 insertions(+), 42 deletions(-) diff --git a/facho/cli.py b/facho/cli.py index 4300277..04458fd 100644 --- a/facho/cli.py +++ b/facho/cli.py @@ -29,6 +29,15 @@ logging.config.dictConfig({ } }) +def disable_ssl(): + # MACHETE + import ssl + if getattr(ssl, '_create_unverified_context', None): + ssl._create_default_https_context = ssl._create_unverified_context + warnings.warn("be sure!! ssl disable") + else: + warnings.warn("can't disable ssl") + # MACHETE se corrige # lxml.etree.DocumentInvalid: Element '{http://www.w3.org/2000/09/xmldsig#}X509SerialNumber': '34255301462796514282327995225552892834' is not a valid value of the atomic type 'xs:integer'. @@ -200,6 +209,20 @@ def validate_invoice(invoice_path): XSD.validate(content, XSD.UBLInvoice) +@click.command() +@click.option('--private-key', type=click.Path(exists=True)) +@click.option('--passphrase') +@click.option('--ssl/--no-ssl', default=False) +@click.argument('xmlfile', type=click.Path(exists=True), required=True) +def sign_xml(private_key, passphrase, xmlfile, ssl=True): + if not ssl: + disable_ssl() + + from facho import fe + signer = fe.DianXMLExtensionSigner(private_key, passphrase=passphrase) + document = open(xmlfile, 'r').read().encode('utf-8') + print(signer.sign_xml_string(document)) + @click.command() @click.option('--private-key', type=click.Path(exists=True)) @click.option('--generate/--validate', default=False) @@ -214,15 +237,9 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr def extensions(form.Invoice): -> List[facho.FachoXMLExtension] """ - # MACHETE if not ssl: - import ssl - if getattr(ssl, '_create_unverified_context', None): - ssl._create_default_https_context = ssl._create_unverified_context - warnings.warn("be sure!! ssl disable") - else: - warnings.warn("can't disable ssl") - + disable_ssl() + import importlib.util spec = importlib.util.spec_from_file_location('invoice', scriptname) @@ -245,11 +262,6 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr extensions = module.extensions(invoice) for extension in extensions: xml.add_extension(extension) - - if private_key: - signer = fe.DianXMLExtensionSigner(private_key, passphrase=passphrase) - xml.add_extension(signer) - print(xml.tostring(xml_declaration=True)) @@ -267,3 +279,4 @@ main.add_command(soap_get_status_zip) main.add_command(soap_get_numbering_range) main.add_command(generate_invoice) main.add_command(validate_invoice) +main.add_command(sign_xml) diff --git a/facho/facho.py b/facho/facho.py index 07fb9ca..b4467bb 100644 --- a/facho/facho.py +++ b/facho/facho.py @@ -98,6 +98,7 @@ class LXMLBuilder: def set_attribute(self, elem, key, value): elem.attrib[key] = value + @classmethod def tostring(self, elem, **attrs): attrs['pretty_print'] = attrs.pop('pretty_print', False) attrs['encoding'] = attrs.pop('encoding', 'UTF-8') diff --git a/facho/fe/fe.py b/facho/fe/fe.py index 880b6a6..0e0d2df 100644 --- a/facho/fe/fe.py +++ b/facho/fe/fe.py @@ -1,7 +1,7 @@ # This file is part of facho. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from ..facho import FachoXML, FachoXMLExtension +from ..facho import FachoXML, FachoXMLExtension, LXMLBuilder import xmlsig import xades from datetime import datetime @@ -187,8 +187,9 @@ class DianXMLExtensionSigner(FachoXMLExtension): def from_pkcs12(self, filepath, password=None): p12 = OpenSSL.crypto.load_pkcs12(open(filepath, 'rb').read(), password) - # return (xpath, xml.Element) - def build(self, fachoxml): + def sign_xml_string(self, document): + xml = LXMLBuilder.from_string(document) + signature = xmlsig.template.create( xmlsig.constants.TransformInclC14N, xmlsig.constants.TransformRsaSha256, @@ -217,31 +218,8 @@ class DianXMLExtensionSigner(FachoXMLExtension): # TODO assert with http://www.sic.gov.co/hora-legal-colombiana props = xades.template.create_signed_properties(qualifying, datetime=datetime.now()) xades.template.add_claimed_role(props, "supplier") - #signed_do = xades.template.ensure_signed_data_object_properties(props) - #xades.template.add_data_object_format( - # signed_do, "#R1", - # identifier=xades.ObjectIdentifier("Idenfitier0", "Description") - #) - #xades.template.add_commitment_type_indication( - # signed_do, - # xades.ObjectIdentifier("Idenfitier0", "Description"), - # qualifiers_type=["Tipo"], - #) - #xades.template.add_commitment_type_indication( - # signed_do, - # xades.ObjectIdentifier("Idenfitier1", references=["#R1"]), - # references=["#R1"], - #) - #xades.template.add_data_object_format( - # signed_do, - # "#RKI", - # description="Desc", - # mime_type="application/xml", - # encoding="UTF-8", - #) - - fachoxml.root.append(signature) + xml.append(signature) policy = xades.policy.GenericPolicyId( self.POLICY_ID, @@ -254,12 +232,20 @@ class DianXMLExtensionSigner(FachoXMLExtension): ctx.sign(signature) ctx.verify(signature) #xmlsig take parent root - fachoxml.root.remove(signature) - + xml.remove(signature) + + fachoxml = FachoXML(xml,nsmap=NAMESPACES) ublextension = fachoxml.fragment('/fe:Invoice/ext:UBLExtensions/ext:UBLExtension', append_not_exists=True) extcontent = ublextension.find_or_create_element('/ext:UBLExtension:/ext:ExtensionContent') fachoxml.append_element(extcontent, signature) + return fachoxml.tostring() + # return (xpath, xml.Element) + def build(self, fachoxml): + xmlsigned = self.sign_xml_string(fachoxml.tostring()) + xml = LXMLBuilder.from_string(xmlsigned) + fachoxml.root = xml + return fachoxml class DianXMLExtensionAuthorizationProvider(FachoXMLExtension): diff --git a/tests/test_fe.py b/tests/test_fe.py index 9318134..ac53aed 100644 --- a/tests/test_fe.py +++ b/tests/test_fe.py @@ -94,5 +94,15 @@ def test_dian_invoice_with_fe(): assert "