Skip to content

Instantly share code, notes, and snippets.

@nehemiascr
Last active November 6, 2018 12:51
Show Gist options
  • Save nehemiascr/a73e0b84cede41c97cdbcfccff610155 to your computer and use it in GitHub Desktop.
Save nehemiascr/a73e0b84cede41c97cdbcfccff610155 to your computer and use it in GitHub Desktop.
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