Last active
November 6, 2018 12:51
-
-
Save nehemiascr/a73e0b84cede41c97cdbcfccff610155 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 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