Last active
November 6, 2018 12:51
-
-
Save nehemiascr/a73e0b84cede41c97cdbcfccff610155 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 base64 | |
| from lxml import etree | |
| from OpenSSL import crypto | |
| import xmlsig | |
| import datetime | |
| import pytz | |
| import hashlib | |
| import urllib | |
| def sign_file(cert, password, request): | |
| min = 1 | |
| max = 99999 | |
| signature_id = 'Signature%05d' % random.randint(min, max) | |
| signed_properties_id = signature_id + '-SignedProperties%05d' \ | |
| % random.randint(min, max) | |
| key_info_id = 'KeyInfo%05d' % random.randint(min, max) | |
| reference_id = 'Reference%05d' % random.randint(min, max) | |
| object_id = 'Object%05d' % random.randint(min, max) | |
| etsi = 'http://uri.etsi.org/01903/v1.3.2#' | |
| sig_policy_identifier = 'https://tribunet.hacienda.go.cr/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf' | |
| sig_policy_hash_value = 'V8lVVNGDCPen6VELRD1Ja8HARFk=' | |
| request = base64.b64decode(request) | |
| _logger.info('request %s' % request) | |
| root = etree.fromstring(request) | |
| sign = xmlsig.template.create( | |
| c14n_method=xmlsig.constants.TransformInclC14N, | |
| sign_method=xmlsig.constants.TransformRsaSha1, | |
| name=signature_id, | |
| ns="ds" | |
| ) | |
| key_info = xmlsig.template.ensure_key_info( | |
| sign, | |
| name=key_info_id | |
| ) | |
| x509_data = xmlsig.template.add_x509_data(key_info) | |
| xmlsig.template.x509_data_add_certificate(x509_data) | |
| xmlsig.template.add_key_value(key_info) | |
| certificate = crypto.load_pkcs12(base64.b64decode(cert), password) | |
| xmlsig.template.add_reference( | |
| sign, | |
| xmlsig.constants.TransformSha1, | |
| uri='#' + signed_properties_id, | |
| uri_type='http://uri.etsi.org/01903#SignedProperties' | |
| ) | |
| xmlsig.template.add_reference( | |
| sign, | |
| xmlsig.constants.TransformSha1, | |
| uri='#' + key_info_id | |
| ) | |
| ref = xmlsig.template.add_reference( | |
| sign, | |
| xmlsig.constants.TransformSha1, | |
| name=reference_id | |
| ) | |
| xmlsig.template.add_transform( | |
| ref, | |
| xmlsig.constants.TransformEnveloped | |
| ) | |
| object_node = etree.SubElement( | |
| sign, | |
| etree.QName(xmlsig.constants.DSigNs, 'Object'), | |
| nsmap={'etsi': etsi}, | |
| attrib={xmlsig.constants.ID_ATTR: object_id} | |
| ) | |
| qualifying_properties = etree.SubElement( | |
| object_node, | |
| etree.QName(etsi, 'QualifyingProperties'), | |
| attrib={ | |
| 'Target': '#' + signature_id | |
| }) | |
| signed_properties = etree.SubElement( | |
| qualifying_properties, | |
| etree.QName(etsi, 'SignedProperties'), | |
| attrib={ | |
| xmlsig.constants.ID_ATTR: signed_properties_id | |
| } | |
| ) | |
| signed_signature_properties = etree.SubElement( | |
| signed_properties, | |
| etree.QName(etsi, 'SignedSignatureProperties') | |
| ) | |
| now = datetime.datetime.now().replace( | |
| microsecond=0, tzinfo=pytz.utc | |
| ) | |
| etree.SubElement( | |
| signed_signature_properties, | |
| etree.QName(etsi, 'SigningTime') | |
| ).text = now.isoformat() | |
| signing_certificate = etree.SubElement( | |
| signed_signature_properties, | |
| etree.QName(etsi, 'SigningCertificate') | |
| ) | |
| signing_certificate_cert = etree.SubElement( | |
| signing_certificate, | |
| etree.QName(etsi, 'Cert') | |
| ) | |
| cert_digest = etree.SubElement( | |
| signing_certificate_cert, | |
| etree.QName(etsi, 'CertDigest') | |
| ) | |
| etree.SubElement( | |
| cert_digest, | |
| etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'), | |
| attrib={ | |
| 'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1' | |
| } | |
| ) | |
| hash_cert = hashlib.sha1( | |
| crypto.dump_certificate( | |
| crypto.FILETYPE_ASN1, | |
| certificate.get_certificate() | |
| ) | |
| ) | |
| etree.SubElement( | |
| cert_digest, | |
| etree.QName(xmlsig.constants.DSigNs, 'DigestValue') | |
| ).text = base64.b64encode(hash_cert.digest()) | |
| issuer_serial = etree.SubElement( | |
| signing_certificate_cert, | |
| etree.QName(etsi, 'IssuerSerial') | |
| ) | |
| etree.SubElement( | |
| issuer_serial, | |
| etree.QName(xmlsig.constants.DSigNs, 'X509IssuerName') | |
| ).text = xmlsig.utils.get_rdns_name( | |
| certificate.get_certificate().to_cryptography().issuer.rdns) | |
| etree.SubElement( | |
| issuer_serial, | |
| etree.QName(xmlsig.constants.DSigNs, 'X509SerialNumber') | |
| ).text = str(certificate.get_certificate().get_serial_number()) | |
| signature_policy_identifier = etree.SubElement( | |
| signed_signature_properties, | |
| etree.QName(etsi, 'SignaturePolicyIdentifier') | |
| ) | |
| signature_policy_id = etree.SubElement( | |
| signature_policy_identifier, | |
| etree.QName(etsi, 'SignaturePolicyId') | |
| ) | |
| sig_policy_id = etree.SubElement( | |
| signature_policy_id, | |
| etree.QName(etsi, 'SigPolicyId') | |
| ) | |
| etree.SubElement( | |
| sig_policy_id, | |
| etree.QName(etsi, 'Identifier') | |
| ).text = sig_policy_identifier | |
| etree.SubElement( | |
| sig_policy_id, | |
| etree.QName(etsi, 'Description') | |
| ).text = "Política de Firma FacturaE v3.1" | |
| sig_policy_hash = etree.SubElement( | |
| signature_policy_id, | |
| etree.QName(etsi, 'SigPolicyHash') | |
| ) | |
| etree.SubElement( | |
| sig_policy_hash, | |
| etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'), | |
| attrib={ | |
| 'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1' | |
| }) | |
| try: | |
| remote = urllib.request.urlopen(sig_policy_identifier) | |
| hash_value = base64.b64encode( | |
| hashlib.sha1(remote.read()).digest()) | |
| except urllib.request.HTTPError: | |
| hash_value = sig_policy_hash_value | |
| etree.SubElement( | |
| sig_policy_hash, | |
| etree.QName(xmlsig.constants.DSigNs, 'DigestValue') | |
| ).text = hash_value | |
| signer_role = etree.SubElement( | |
| signed_signature_properties, | |
| etree.QName(etsi, 'SignerRole') | |
| ) | |
| claimed_roles = etree.SubElement( | |
| signer_role, | |
| etree.QName(etsi, 'ClaimedRoles') | |
| ) | |
| etree.SubElement( | |
| claimed_roles, | |
| etree.QName(etsi, 'ClaimedRole') | |
| ).text = 'supplier' | |
| signed_data_object_properties = etree.SubElement( | |
| signed_properties, | |
| etree.QName(etsi, 'SignedDataObjectProperties') | |
| ) | |
| data_object_format = etree.SubElement( | |
| signed_data_object_properties, | |
| etree.QName(etsi, 'DataObjectFormat'), | |
| attrib={ | |
| 'ObjectReference': '#' + reference_id | |
| } | |
| ) | |
| etree.SubElement( | |
| data_object_format, | |
| etree.QName(etsi, 'Description') | |
| ).text = 'Factura' | |
| etree.SubElement( | |
| data_object_format, | |
| etree.QName(etsi, 'MimeType') | |
| ).text = 'text/xml' | |
| ctx = xmlsig.SignatureContext() | |
| key = crypto.load_pkcs12(base64.b64decode(cert), password) | |
| ctx.x509 = key.get_certificate().to_cryptography() | |
| ctx.public_key = ctx.x509.public_key() | |
| ctx.private_key = key.get_privatekey().to_cryptography_key() | |
| root.append(sign) | |
| ctx.sign(sign) | |
| return etree.tostring( | |
| root, xml_declaration=True, encoding='UTF-8' | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment