I found Xades Signature for Python GitHub. My plan is to apply Xades-EPES signature to XML files. According to the work from GitHub, it is capable to perform this process, but I could not make this run.
test_xades.py
has two methods. I receive a error message when I try to run it. Well, the issue is that I am not sure about if the lib can sign Xades-EPES or how to achieve it.
CODE:
import unittest
from datetime import datetime
from os import path
from OpenSSL import crypto
import xmlsig
from xades import XAdESContext, template, utils, ObjectIdentifier
from xades.policy import GenericPolicyId, ImpliedPolicy
from basex import parse_xml, BASE_DIR
class TestXadesSignature(unittest.TestCase):
def test_verify(self):
root = parse_xml('data/sample.xml')
sign = root.xpath(
'//ds:Signature', namespaces={'ds': xmlsig.constants.DSigNs}
)[0]
ctx = XAdESContext()
ctx.verify(sign)
def test_sign(self):
root = parse_xml('data/unsigned-sample.xml')
sign = root.xpath(
'//ds:Signature', namespaces={'ds': xmlsig.constants.DSigNs}
)[0]
policy = GenericPolicyId(
'http://www.facturae.es/politica_de_firma_formato_facturae/'
'politica_de_firma_formato_facturae_v3_1.pdf',
u"Politica de Firma FacturaE v3.1",
xmlsig.constants.TransformSha1
)
ctx = XAdESContext(policy)
with open(path.join(BASE_DIR, "data/keyStore.p12"), "rb") as key_file:
ctx.load_pkcs12(crypto.load_pkcs12(key_file.read()))
ctx.sign(sign)
ctx.verify(sign)
def test_create(self):
root = parse_xml('data/free-sample.xml')
signature = xmlsig.template.create(
xmlsig.constants.TransformInclC14N,
xmlsig.constants.TransformRsaSha1,
"Signature"
)
signature_id = utils.get_unique_id()
ref = xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha1, uri="", name="REF"
)
xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha1, uri="#KI"
)
xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha1, uri="#" + signature_id
)
ki = xmlsig.template.ensure_key_info(signature, name='KI')
data = xmlsig.template.add_x509_data(ki)
xmlsig.template.x509_data_add_certificate(data)
serial = xmlsig.template.x509_data_add_issuer_serial(data)
xmlsig.template.x509_issuer_serial_add_issuer_name(serial)
xmlsig.template.x509_issuer_serial_add_serial_number(serial)
xmlsig.template.add_key_value(ki)
qualifying = template.create_qualifying_properties(
signature, name=utils.get_unique_id()
)
props = template.create_signed_properties(
qualifying, name=signature_id
)
template.add_claimed_role(props, "Supp2")
template.add_production_place(props, city='Madrid')
template.add_production_place(
props, state='BCN', postal_code='08000', country='ES')
template.add_claimed_role(props, "Supp")
policy = GenericPolicyId(
'http://www.facturae.es/politica_de_firma_formato_facturae/'
'politica_de_firma_formato_facturae_v3_1.pdf',
u"Politica de Firma FacturaE v3.1",
xmlsig.constants.TransformSha1
)
root.append(signature)
ctx = XAdESContext(policy)
with open(path.join(BASE_DIR, "data/keyStore.p12"), "rb") as key_file:
ctx.load_pkcs12(crypto.load_pkcs12(key_file.read()))
ctx.sign(signature)
ctx.verify(signature)
def test_create_2(self):
root = parse_xml('data/free-sample.xml')
signature = xmlsig.template.create(
xmlsig.constants.TransformInclC14N,
xmlsig.constants.TransformRsaSha1,
"Signature"
)
ref = xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha1, uri="", name="R1"
)
xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha1, uri="#KI", name="RKI"
)
ki = xmlsig.template.ensure_key_info(signature, name='KI')
data = xmlsig.template.add_x509_data(ki)
xmlsig.template.x509_data_add_certificate(data)
serial = xmlsig.template.x509_data_add_issuer_serial(data)
xmlsig.template.x509_issuer_serial_add_issuer_name(serial)
xmlsig.template.x509_issuer_serial_add_serial_number(serial)
xmlsig.template.add_key_value(ki)
qualifying = template.create_qualifying_properties(signature)
utils.ensure_id(qualifying)
utils.ensure_id(qualifying)
props = template.create_signed_properties(
qualifying, datetime=datetime.now()
)
template.add_claimed_role(props, "Supp")
signed_do = template.ensure_signed_data_object_properties(props)
template.add_data_object_format(
signed_do,
"#R1",
identifier=ObjectIdentifier("Idenfitier0", "Description")
)
template.add_commitment_type_indication(
signed_do,
ObjectIdentifier("Idenfitier0", "Description"),
qualifiers_type=["Tipo"]
)
template.add_commitment_type_indication(
signed_do,
ObjectIdentifier("Idenfitier1", references=["#R1"]),
references=["#R1"]
)
template.add_data_object_format(
signed_do,
"#RKI",
description="Desc",
mime_type="application/xml",
encoding='UTF-8'
)
root.append(signature)
ctx = XAdESContext(ImpliedPolicy(xmlsig.constants.TransformSha1))
with open(path.join(BASE_DIR, "data/keyStore.p12"), "rb") as key_file:
ctx.load_pkcs12(crypto.load_pkcs12(key_file.read()))
ctx.sign(signature)
from lxml import etree
print(etree.tostring(root))
ctx.verify(signature)
x= TestXadesSignature()
x.test_create()
x.test_create_2()
TRACEBACK:
Exception has occurred: StopIteration
exception: no description
File "/home/sergio/Escritorio/PROYECTOSMAY2018/haciendaPython/src/lxml/lxml.etree.pyx", line 2821, in lxml.etree._ElementMatchIterator.__next__ (src/lxml/lxml.etree.c:75265)
File "/home/sergio/Escritorio/PROYECTOSMAY2018/haciendaPython/haciendaCode/pythoncorella/test/test_xades.py", line 50, in test_create
xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
File "/home/sergio/Escritorio/PROYECTOSMAY2018/haciendaPython/haciendaCode/pythoncorella/test/test_xades.py", line 150, in <module>
x.test_create()
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/usr/lib/python3.6/runpy.py", line 96, in _run_module_code
mod_name, mod_spec, pkg_name, script_name)
File "/usr/lib/python3.6/runpy.py", line 263, in run_path
pkg_name=pkg_name, script_name=fname)
Use this imports
import xmlsig
from lxml import etree
from OpenSSL import crypto
from xades import XAdESContext, template, utils
from xades.policy import GenericPolicyId
GLOBAL IS THIS
POLICY_ENDPOINT = "politicadefirma/v2/politicadefirmav2.pdf"
SIGN_POLICY = f"http://facturaelectronica.dian.gov.co/{POLICY_ENDPOINT}"
CERTICAMARA_PFX = os.environ.get(
'CERTICAMARA_PFX',
'/path/to/certificate.pfx')
you need to parse the xml file you want to sign
parsed_file = etree.parse(file_path).getroot()
Create Signature template with the corresponding Transform stuff
signature = xmlsig.template.create(
xmlsig.constants.TransformInclC14N,
xmlsig.constants.TransformRsaSha256,
"Signature",
)
create an uuid and the reference to the signature
signature_id = utils.get_unique_id()
ref = xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha256, uri="", name="REF"
)
xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped)
xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha256, uri="#" + signature_id
)
xmlsig.template.add_reference(
signature, xmlsig.constants.TransformSha256, uri="#" + signature_id
)
ki = xmlsig.template.ensure_key_info(signature, name="KI")
data = xmlsig.template.add_x509_data(ki)
xmlsig.template.x509_data_add_certificate(data)
serial = xmlsig.template.x509_data_add_issuer_serial(data)
xmlsig.template.x509_issuer_serial_add_issuer_name(serial)
xmlsig.template.x509_issuer_serial_add_serial_number(serial)
xmlsig.template.add_key_value(ki)
qualifying = template.create_qualifying_properties(
signature, name=utils.get_unique_id(), etsi='xades'
)
props = template.create_signed_properties(qualifying, name=signature_id)
# Additional data for signature
template.add_claimed_role(props, "Supp2")
template.add_production_place(props, city="Bogotá Colombia")
template.add_production_place(
props, state="BCN", postal_code="08000", country="CO"
)
template.add_claimed_role(props, "SNE")
policy = GenericPolicyId(
SIGN_POLICY,
u"Política de firma para facturas"
" electrónicas de la República de Colombia",
xmlsig.constants.TransformSha256,
)
parsed_file.append(signature)
with open(CERTICAMARA_ANDES_PFX, "rb") as key_file:
certificate = crypto.load_pkcs12(key_file.read(), 'filepassword')
ctx = XAdESContext(
policy,
certificate.get_certificate().to_cryptography(),
)
ctx.load_pkcs12(certificate)
ctx.sign(signature)
parsed_file[0][position][0].append(signature)
et = etree.ElementTree(parsed_file)
nfs_name = 'Name of the signed file'
et.write(nfs_name, pretty_print=True,
encoding='utf-8', xml_declaration=True)
I used this for a project for electronic invoice signing in my country, take into account that the source code of Enric Tobella is ahead compared to the package. With the source code you can add multiple certificates whereas the package only let you use one, that was the only difference I found.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With