xml digital signature in python
I'm trying to add a digital signature for an electronic invoice in my country. Signature use XADES-EPS as standard.
I'm using xmlsig and etree to create the signtaure sintax in python 3, like this
def sign_file(cert, password, xml_firma):
min = 1
max = 99999
xmlns_uris = {'ds': 'http://myhost.com/p.xsd'}
random_val = random.randint(min, max)
signature_id = 'Signature-' + str(random_val)
#signed_properties_id = signature_id + '-SignedProperties%05d'
# % random.randint(min, max)
signed_properties_id = 'SignedProperties-' + signature_id
signature_value = 'SignatureValue-' + str(random_val)
qualifying_properties = 'QualifyingProperties-%05d' % random.randint(min, max)
#key_info_id = 'KeyInfo%05d' % random.randint(min, max)
key_info_id = 'KeyInfoId-' + signature_id
reference_id = 'Reference-%05d' % random.randint(min, max)
#object_id = 'Object%05d' % random.randint(min, max)
object_id = 'XadesObjectId-%05d' % random.randint(min, max)
xades = 'http://uri.etsi.org/01903/v1.3.2#'
ds = 'http://www.w3.org/2000/09/xmldsig#'
xades141 = 'http://uri.etsi.org/01903/v1.4.1#'
sig_policy_identifier = 'https://www.hacienda.go.cr/ATV/ComprobanteElectronico/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf'
sig_policy_hash_value = 'V8lVVNGDCPen6VELRD1Ja8HARFk='
xml_firmar = base64decode(xml_firma)
root = etree.fromstring(xml_firmar)
certificate = crypto.load_pkcs12(base64.b64decode(cert), password)
# GENERAR NODO PERSONALIZADO PARA FE
sign = etree.Element(
etree.QName(ds, 'Signature'),
nsmap={'ds': ds},
attrib={
xmlsig.constants.ID_ATTR: signature_id,
}
)
#annotate_with_xmlns_prefixes(sign, 'ds')
#add_xmnls_attributes(sign, xmlns_uris)
# GENERO EL NODO ds:SignedInfo
signed_info = etree.SubElement(
sign,
etree.QName(ds, 'SignedInfo')
)
# CREO EL NODO ds:CanonicalizationMethod DENTRO DE signed_info
etree.SubElement(
signed_info,
etree.QName(ds, 'CanonicalizationMethod'),
attrib={
'Algorithm': xmlsig.constants.TransformInclC14N
}
)
# CREO EL NODO ds:SignatureMethod DENTRO DE signed_info
etree.SubElement(
signed_info,
etree.QName(ds, 'SignatureMethod'),
attrib={
'Algorithm': xmlsig.constants.TransformRsaSha256
}
)
# CREO EL NODO ds:SignatureMethod DENTRO DE signed_info
reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
xmlsig.constants.ID_ATTR: reference_id,
'URI': ''
}
)
# CREO EL NODO ds:Transforms DENTRO DE reference
transforms = etree.SubElement(
reference,
etree.QName(ds, 'Transforms'),
)
# CREO EL NODO ds:Transform DENTRO DE trasnforms
etree.SubElement(
transforms,
etree.QName(ds, 'Transform'),
attrib={
'Algorithm': 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value.digest())
# CREO EL SEGUNDO NODO ds:SignatureMethod DENTRO DE signed_info
sec_reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
xmlsig.constants.ID_ATTR: 'ReferenceKeyInfo',
'URI': '#' + key_info_id
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
sec_reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value2 = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
sec_reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value2.digest())
# CREO EL TERCER NODO ds:Reference DENTRO DE signed_info
tr_reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
'Type': 'http://uri.etsi.org/01903#SignedProperties',
'URI': '#' + signed_properties_id,
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
tr_reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value3 = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
tr_reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value3.digest())
# GENERO EL NODO ds:SignatureValue
etree.SubElement(
sign,
etree.QName(ds, 'SignatureValue'),
attrib={
xmlsig.constants.ID_ATTR: signature_value
}
)
# GENERO EL NODO ds:KeyInfo
key_info = etree.SubElement(
sign,
etree.QName(ds, 'KeyInfo'),
attrib={
xmlsig.constants.ID_ATTR: key_info_id
}
)
# GENERO EL NODO ds:X509Data
x509 = etree.SubElement(
key_info,
etree.QName(ds, 'X509Data'),
)
# GENERO EL NODO ds:X509Certificate
etree.SubElement(
x509,
etree.QName(ds, 'X509Certificate'),
)
# GENERO EL NODO ds:KeyValue
etree.SubElement(
key_info,
etree.QName(ds, 'KeyValue'),
)
#AQUI EMPIEZO A CREAR EL NODO DE QUALIFYNG PROPERTIES
object_node = etree.SubElement(
sign,
etree.QName(xmlsig.constants.DSigNs, 'Object'),
#nsmap={'etsi': etsi},
attrib={xmlsig.constants.ID_ATTR: object_id}
#nsmap={'xades': xades},
#attrib={xmlsig.constants.ID_ATTR: object_id} NO SE NECESITA EN EL XML DE HACIENDA
)
#CREO EL SUBNODO QUALIFYING PROPERTIES
qualifying_properties = etree.SubElement(
object_node,
etree.QName(xades, 'QualifyingProperties'),
nsmap = {'xades': xades, 'xades141': xades141},
attrib={
xmlsig.constants.ID_ATTR: qualifying_properties,
'Target': '#' + signature_id
})
#CREO EL NODO xades:SignedProperties DENTRO DE QUALIFYING PROPERTIES
signed_properties = etree.SubElement(
qualifying_properties,
etree.QName(xades, 'SignedProperties'),
attrib={
xmlsig.constants.ID_ATTR: signed_properties_id #ESTO HAY QUE CAMBIARLO PARA QUE SEA COMO LO PIDE HACIENDA
}
)
# CREO EL NODO xades:SignedSignatureProperties DENTRO DE SIGNED PROPERTIES
signed_signature_properties = etree.SubElement(
signed_properties,
etree.QName(xades, 'SignedSignatureProperties')
)
#GENERO LA HORA PARA EL NODO xades:SigningTime
#now = datetime.datetime.now().replace(
# microsecond=0, tzinfo=pytz.utc
#)
# GENERO EL NODO xades:SigningTime Y LE PONGO LA HORA
etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SigningTime')
).text = get_time_hacienda()
#GENERO EL NODO xades:SigningCertificate
signing_certificate = etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SigningCertificate')
)
#GENERO EL NODO xades:Cert DENTRO DE xades:SigningCertificate
signing_certificate_cert = etree.SubElement(
signing_certificate,
etree.QName(xades, 'Cert')
)
#GENERO EL NODO xades:CertDigest DENTRO DE xades:cert
cert_digest = etree.SubElement(
signing_certificate_cert,
etree.QName(xades, 'CertDigest')
)
#GENERO EL NODO ds:DigestMethod DENTRO DE xades:CertDigest
etree.SubElement(
cert_digest,
etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
hash_cert = hashlib.sha256(
crypto.dump_certificate(
crypto.FILETYPE_ASN1,
certificate.get_certificate()
)
)
# GENERO EL NODO ds:DigestValue DENTRO DE xades:CertDigest Y LE PONGO EL VALOR DEL DIGESTVALUE ANTERIOR
etree.SubElement(
cert_digest,
etree.QName(xmlsig.constants.DSigNs, 'DigestValue')
).text = base64.b64encode(hash_cert.digest())
# GENERO EL NODO xades:IssuerSerial DENTRO DE xades:Cert
issuer_serial = etree.SubElement(
signing_certificate_cert,
etree.QName(xades, 'IssuerSerial')
)
# GENERO EL NODO ds:X509IssuerName DENTRO DE xades:IssuerSerial
etree.SubElement(
issuer_serial,
etree.QName(xmlsig.constants.DSigNs, 'X509IssuerName')
).text = xmlsig.utils.get_rdns_name(certificate.get_certificate().to_cryptography().issuer.rdns)
# GENERO EL NODO ds:X509SerialNumber DENTRO DE xades:IssuerSerial
etree.SubElement(
issuer_serial,
etree.QName(xmlsig.constants.DSigNs, 'X509SerialNumber')
).text = str(certificate.get_certificate().get_serial_number())
# GENERO EL NODO xades:SignaturePolicyIdentifier DENTRO DE sign
signature_policy_identifier = etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SignaturePolicyIdentifier')
)
# GENERO EL NODO xades:SignaturePolicyId DENTRO DE xades:SignaturePolicyIdentifier
signature_policy_id = etree.SubElement(
signature_policy_identifier,
etree.QName(xades, 'SignaturePolicyId')
)
# GENERO EL NODO xades:SigPolicyId DENTRO DE xades:SignaturePolicyId
sig_policy_id = etree.SubElement(
signature_policy_id,
etree.QName(xades, 'SigPolicyId')
)
# GENERO EL NODO xades:Identifier DENTRO DE xades:SigPolicyId
etree.SubElement(
sig_policy_id,
etree.QName(xades, 'Identifier')
).text = sig_policy_identifier
#BORRO ESTE NODO PUES EN FE COSTA RICA NO SE NECESITA
etree.SubElement(
sig_policy_id,
etree.QName(xades, 'Description')
)#.text = "Política de Firma FacturaE v3.1"
# GENERO EL NODO xades:Identifier DENTRO DE signature_policy_id
sig_policy_hash = etree.SubElement(
signature_policy_id,
etree.QName(xades, 'SigPolicyHash')
)
# GENERO EL NODO ds:DigestMethod DENTRO DE xades:Identifier
etree.SubElement(
sig_policy_hash,
etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1'
})
#GENERO EL DIGEST PARA EL CERTIFICADO LEYENDOLO DE LA URL DE HACIENDA
try:
remote = urllib.request.urlopen(sig_policy_identifier)
hash_value = base64.b64encode(hashlib.sha256(remote.read()).digest())
except urllib.request.HTTPError:
hash_value = sig_policy_hash_value
# GENERO EL NODO ds:DigestValue ds:DigestMethod DENTRO DE sig_policy_hash
etree.SubElement(
sig_policy_hash,
etree.QName(xmlsig.constants.DSigNs, 'DigestValue')
).text = hash_value
#NO REQUERIDO PARA FE COSTA RICA
#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(xades, 'SignedDataObjectProperties')
)
data_object_format = etree.SubElement(
signed_data_object_properties,
etree.QName(xades, 'DataObjectFormat'),
attrib={
'ObjectReference': '#' + reference_id
}
)
#etree.SubElement(
# data_object_format,
# etree.QName(etsi, 'Description')
#).text = 'Factura'
etree.SubElement(
data_object_format,
etree.QName(xades, '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)
root.xpath(
'//ds:Signature', namespaces={'ds': xmlsig.constants.DSigNs}
)[0]
ctx.sign(sign)
#xml_bytes = etree.tostring(root, xml_declaration=True)
#return stringToBase64(xml_bytes)
return etree.tostring(
root
)
That code produces a xml signature in the following way
<ds:Signature Id="Signature-17044">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference Id="Reference-74608" URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>+fhrrmGMWpvy8IEowaa2fLh905rLv6xRmKB4zD6Omtg=</ds:DigestValue>
</ds:Reference>
<ds:Reference Id="ReferenceKeyInfo" URI="#KeyInfoId-Signature-17044">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>zXjLpycv3XvGI47RJR0qIoxtZoApAHwkdjDqRKiGpTU=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#SignedProperties-Signature-17044">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>sHX8p3YfyvxNIwL2NC54sZdYfvsfXvfYTCks/8BMQlQ=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="SignatureValue-17044">B9Mb3Ogm1QNkpu1N8o4rF0EP4a/vM9Yjgk7EiYZBQmU/r6+yZ/pVC27XYJubbcoxhlth1pk4y15d95I7RfqdqOwMt8dj6P9FKljPH2jCRU0IlHCul3eudjaBWfbu2+fjpyIFRO53kHhOfumkstMKtSCAPSgHRV0wLb06JcZhVCUXUNy+rJfElxzJjhNq+aHcIjayf2TVs1w/elFOz9Ax2v18xol6BRyHnlrQEUtD57AiVurkkcGs0oUqrDGV7IN3YebBJWge/ttXF+5Bz9NnBHQD4iU5ul3aGhcOwq7FY7qxIbmqBHhmcLi7ZGVCXfiEU58Sn6Sa8yBjOILh5Wgzng==</ds:SignatureValue>
<ds:KeyInfo Id="KeyInfoId-Signature-17044">
<ds:X509Data>
<ds:X509Certificate>MIIFRjCCAy6gAwIBAgIGAWZEbfKCMA0GCSqGSIb3DQEBCwUAMG4xCzAJBgNVBAYTAkNSMSkwJwYDVQQKDCBNSU5JU1RFUklPIERFIEhBQ0lFTkRBIC0gU0FOREJPWDEMMAoGA1UECwwD</ds:X509Certificate>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>pVc+nKRrS6q2O05DEO7r1smiY/0PDTZpYSirjEzPaxQ2hRvZDGaxX8wEzqiAE7i9icZbqtwaQPECBRhlzjEK4lxbDhESqYI34CKiMMeARquVGn9WJ/EQjQhGm1KgqROvizX3LRcxDgJzd6pbKPxqTUC/gDAy/PEJJuD9WOH0NVYCREZtD3ShkiIt3DphbDK84Whqhvt47ZyFRSIgjxrRkerhOZj3EzFjr4aIADXUXY2LJGTyKHEe8WcvA9k/aaZGZaTRINOwgk4KMSzNXBDweoxXzN0bJukbxXEicLqO7cJHqoaZX0gCDU8KfAN2UAtmhQ/IRgkSD6O2F72kC86ePQ==</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
<ds:Object Id="XadesObjectId-41822">
<xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" Id="QualifyingProperties-01611" Target="#Signature-17044">
<xades:SignedProperties Id="SignedProperties-Signature-17044">
<xades:SignedSignatureProperties>
<xades:SigningTime>2018-11-12T15:24:21-06:00</xades:SigningTime>
<xades:SigningCertificate>
<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>gVmykfOS2A4p8ALJ6XVLcdTagMz+hMG4ikjzFipgymI=</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>CN=CA PERSONA JURIDICA - SANDBOX,OU=DGT,O=MINISTERIO DE HACIENDA - SANDBOX,C=CR</ds:X509IssuerName>
<ds:X509SerialNumber>1538746348162</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>
</xades:SigningCertificate>
<xades:SignaturePolicyIdentifier>
<xades:SignaturePolicyId>
<xades:SigPolicyId>
<xades:Identifier>https://www.hacienda.go.cr/ATV/ComprobanteElectronico/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf</xades:Identifier>
<xades:Description/>
</xades:SigPolicyId>
<xades:SigPolicyHash>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>a5aV6NckKzC/0CR4tQeTg2ULnhUNK2uDxsO5VuRInTE=</ds:DigestValue>
</xades:SigPolicyHash>
</xades:SignaturePolicyId>
</xades:SignaturePolicyIdentifier>
</xades:SignedSignatureProperties>
<xades:SignedDataObjectProperties>
<xades:DataObjectFormat ObjectReference="#Reference-74608">
<xades:MimeType>text/xml</xades:MimeType>
</xades:DataObjectFormat>
</xades:SignedDataObjectProperties>
</xades:SignedProperties>
</xades:QualifyingProperties>
</ds:Object>
</ds:Signature>
Xml result has the i need but i get alway invalida signature response. While checking the signature this is the response i get
Checking signature...
xmlsec:
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=250:obj=sha256:subj=unknown:error=12:invalid data:data and digest do not match
FAIL
SignedInfo References (ok/all): 1/2
Manifests References (ok/all): 0/0
but i have no idea what the problem can be
Any ideas?
NOTE: CERTIFICATE INFORMATION HAS BEEN CUTED
python xml signature
add a comment |
I'm trying to add a digital signature for an electronic invoice in my country. Signature use XADES-EPS as standard.
I'm using xmlsig and etree to create the signtaure sintax in python 3, like this
def sign_file(cert, password, xml_firma):
min = 1
max = 99999
xmlns_uris = {'ds': 'http://myhost.com/p.xsd'}
random_val = random.randint(min, max)
signature_id = 'Signature-' + str(random_val)
#signed_properties_id = signature_id + '-SignedProperties%05d'
# % random.randint(min, max)
signed_properties_id = 'SignedProperties-' + signature_id
signature_value = 'SignatureValue-' + str(random_val)
qualifying_properties = 'QualifyingProperties-%05d' % random.randint(min, max)
#key_info_id = 'KeyInfo%05d' % random.randint(min, max)
key_info_id = 'KeyInfoId-' + signature_id
reference_id = 'Reference-%05d' % random.randint(min, max)
#object_id = 'Object%05d' % random.randint(min, max)
object_id = 'XadesObjectId-%05d' % random.randint(min, max)
xades = 'http://uri.etsi.org/01903/v1.3.2#'
ds = 'http://www.w3.org/2000/09/xmldsig#'
xades141 = 'http://uri.etsi.org/01903/v1.4.1#'
sig_policy_identifier = 'https://www.hacienda.go.cr/ATV/ComprobanteElectronico/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf'
sig_policy_hash_value = 'V8lVVNGDCPen6VELRD1Ja8HARFk='
xml_firmar = base64decode(xml_firma)
root = etree.fromstring(xml_firmar)
certificate = crypto.load_pkcs12(base64.b64decode(cert), password)
# GENERAR NODO PERSONALIZADO PARA FE
sign = etree.Element(
etree.QName(ds, 'Signature'),
nsmap={'ds': ds},
attrib={
xmlsig.constants.ID_ATTR: signature_id,
}
)
#annotate_with_xmlns_prefixes(sign, 'ds')
#add_xmnls_attributes(sign, xmlns_uris)
# GENERO EL NODO ds:SignedInfo
signed_info = etree.SubElement(
sign,
etree.QName(ds, 'SignedInfo')
)
# CREO EL NODO ds:CanonicalizationMethod DENTRO DE signed_info
etree.SubElement(
signed_info,
etree.QName(ds, 'CanonicalizationMethod'),
attrib={
'Algorithm': xmlsig.constants.TransformInclC14N
}
)
# CREO EL NODO ds:SignatureMethod DENTRO DE signed_info
etree.SubElement(
signed_info,
etree.QName(ds, 'SignatureMethod'),
attrib={
'Algorithm': xmlsig.constants.TransformRsaSha256
}
)
# CREO EL NODO ds:SignatureMethod DENTRO DE signed_info
reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
xmlsig.constants.ID_ATTR: reference_id,
'URI': ''
}
)
# CREO EL NODO ds:Transforms DENTRO DE reference
transforms = etree.SubElement(
reference,
etree.QName(ds, 'Transforms'),
)
# CREO EL NODO ds:Transform DENTRO DE trasnforms
etree.SubElement(
transforms,
etree.QName(ds, 'Transform'),
attrib={
'Algorithm': 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value.digest())
# CREO EL SEGUNDO NODO ds:SignatureMethod DENTRO DE signed_info
sec_reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
xmlsig.constants.ID_ATTR: 'ReferenceKeyInfo',
'URI': '#' + key_info_id
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
sec_reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value2 = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
sec_reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value2.digest())
# CREO EL TERCER NODO ds:Reference DENTRO DE signed_info
tr_reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
'Type': 'http://uri.etsi.org/01903#SignedProperties',
'URI': '#' + signed_properties_id,
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
tr_reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value3 = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
tr_reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value3.digest())
# GENERO EL NODO ds:SignatureValue
etree.SubElement(
sign,
etree.QName(ds, 'SignatureValue'),
attrib={
xmlsig.constants.ID_ATTR: signature_value
}
)
# GENERO EL NODO ds:KeyInfo
key_info = etree.SubElement(
sign,
etree.QName(ds, 'KeyInfo'),
attrib={
xmlsig.constants.ID_ATTR: key_info_id
}
)
# GENERO EL NODO ds:X509Data
x509 = etree.SubElement(
key_info,
etree.QName(ds, 'X509Data'),
)
# GENERO EL NODO ds:X509Certificate
etree.SubElement(
x509,
etree.QName(ds, 'X509Certificate'),
)
# GENERO EL NODO ds:KeyValue
etree.SubElement(
key_info,
etree.QName(ds, 'KeyValue'),
)
#AQUI EMPIEZO A CREAR EL NODO DE QUALIFYNG PROPERTIES
object_node = etree.SubElement(
sign,
etree.QName(xmlsig.constants.DSigNs, 'Object'),
#nsmap={'etsi': etsi},
attrib={xmlsig.constants.ID_ATTR: object_id}
#nsmap={'xades': xades},
#attrib={xmlsig.constants.ID_ATTR: object_id} NO SE NECESITA EN EL XML DE HACIENDA
)
#CREO EL SUBNODO QUALIFYING PROPERTIES
qualifying_properties = etree.SubElement(
object_node,
etree.QName(xades, 'QualifyingProperties'),
nsmap = {'xades': xades, 'xades141': xades141},
attrib={
xmlsig.constants.ID_ATTR: qualifying_properties,
'Target': '#' + signature_id
})
#CREO EL NODO xades:SignedProperties DENTRO DE QUALIFYING PROPERTIES
signed_properties = etree.SubElement(
qualifying_properties,
etree.QName(xades, 'SignedProperties'),
attrib={
xmlsig.constants.ID_ATTR: signed_properties_id #ESTO HAY QUE CAMBIARLO PARA QUE SEA COMO LO PIDE HACIENDA
}
)
# CREO EL NODO xades:SignedSignatureProperties DENTRO DE SIGNED PROPERTIES
signed_signature_properties = etree.SubElement(
signed_properties,
etree.QName(xades, 'SignedSignatureProperties')
)
#GENERO LA HORA PARA EL NODO xades:SigningTime
#now = datetime.datetime.now().replace(
# microsecond=0, tzinfo=pytz.utc
#)
# GENERO EL NODO xades:SigningTime Y LE PONGO LA HORA
etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SigningTime')
).text = get_time_hacienda()
#GENERO EL NODO xades:SigningCertificate
signing_certificate = etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SigningCertificate')
)
#GENERO EL NODO xades:Cert DENTRO DE xades:SigningCertificate
signing_certificate_cert = etree.SubElement(
signing_certificate,
etree.QName(xades, 'Cert')
)
#GENERO EL NODO xades:CertDigest DENTRO DE xades:cert
cert_digest = etree.SubElement(
signing_certificate_cert,
etree.QName(xades, 'CertDigest')
)
#GENERO EL NODO ds:DigestMethod DENTRO DE xades:CertDigest
etree.SubElement(
cert_digest,
etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
hash_cert = hashlib.sha256(
crypto.dump_certificate(
crypto.FILETYPE_ASN1,
certificate.get_certificate()
)
)
# GENERO EL NODO ds:DigestValue DENTRO DE xades:CertDigest Y LE PONGO EL VALOR DEL DIGESTVALUE ANTERIOR
etree.SubElement(
cert_digest,
etree.QName(xmlsig.constants.DSigNs, 'DigestValue')
).text = base64.b64encode(hash_cert.digest())
# GENERO EL NODO xades:IssuerSerial DENTRO DE xades:Cert
issuer_serial = etree.SubElement(
signing_certificate_cert,
etree.QName(xades, 'IssuerSerial')
)
# GENERO EL NODO ds:X509IssuerName DENTRO DE xades:IssuerSerial
etree.SubElement(
issuer_serial,
etree.QName(xmlsig.constants.DSigNs, 'X509IssuerName')
).text = xmlsig.utils.get_rdns_name(certificate.get_certificate().to_cryptography().issuer.rdns)
# GENERO EL NODO ds:X509SerialNumber DENTRO DE xades:IssuerSerial
etree.SubElement(
issuer_serial,
etree.QName(xmlsig.constants.DSigNs, 'X509SerialNumber')
).text = str(certificate.get_certificate().get_serial_number())
# GENERO EL NODO xades:SignaturePolicyIdentifier DENTRO DE sign
signature_policy_identifier = etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SignaturePolicyIdentifier')
)
# GENERO EL NODO xades:SignaturePolicyId DENTRO DE xades:SignaturePolicyIdentifier
signature_policy_id = etree.SubElement(
signature_policy_identifier,
etree.QName(xades, 'SignaturePolicyId')
)
# GENERO EL NODO xades:SigPolicyId DENTRO DE xades:SignaturePolicyId
sig_policy_id = etree.SubElement(
signature_policy_id,
etree.QName(xades, 'SigPolicyId')
)
# GENERO EL NODO xades:Identifier DENTRO DE xades:SigPolicyId
etree.SubElement(
sig_policy_id,
etree.QName(xades, 'Identifier')
).text = sig_policy_identifier
#BORRO ESTE NODO PUES EN FE COSTA RICA NO SE NECESITA
etree.SubElement(
sig_policy_id,
etree.QName(xades, 'Description')
)#.text = "Política de Firma FacturaE v3.1"
# GENERO EL NODO xades:Identifier DENTRO DE signature_policy_id
sig_policy_hash = etree.SubElement(
signature_policy_id,
etree.QName(xades, 'SigPolicyHash')
)
# GENERO EL NODO ds:DigestMethod DENTRO DE xades:Identifier
etree.SubElement(
sig_policy_hash,
etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1'
})
#GENERO EL DIGEST PARA EL CERTIFICADO LEYENDOLO DE LA URL DE HACIENDA
try:
remote = urllib.request.urlopen(sig_policy_identifier)
hash_value = base64.b64encode(hashlib.sha256(remote.read()).digest())
except urllib.request.HTTPError:
hash_value = sig_policy_hash_value
# GENERO EL NODO ds:DigestValue ds:DigestMethod DENTRO DE sig_policy_hash
etree.SubElement(
sig_policy_hash,
etree.QName(xmlsig.constants.DSigNs, 'DigestValue')
).text = hash_value
#NO REQUERIDO PARA FE COSTA RICA
#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(xades, 'SignedDataObjectProperties')
)
data_object_format = etree.SubElement(
signed_data_object_properties,
etree.QName(xades, 'DataObjectFormat'),
attrib={
'ObjectReference': '#' + reference_id
}
)
#etree.SubElement(
# data_object_format,
# etree.QName(etsi, 'Description')
#).text = 'Factura'
etree.SubElement(
data_object_format,
etree.QName(xades, '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)
root.xpath(
'//ds:Signature', namespaces={'ds': xmlsig.constants.DSigNs}
)[0]
ctx.sign(sign)
#xml_bytes = etree.tostring(root, xml_declaration=True)
#return stringToBase64(xml_bytes)
return etree.tostring(
root
)
That code produces a xml signature in the following way
<ds:Signature Id="Signature-17044">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference Id="Reference-74608" URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>+fhrrmGMWpvy8IEowaa2fLh905rLv6xRmKB4zD6Omtg=</ds:DigestValue>
</ds:Reference>
<ds:Reference Id="ReferenceKeyInfo" URI="#KeyInfoId-Signature-17044">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>zXjLpycv3XvGI47RJR0qIoxtZoApAHwkdjDqRKiGpTU=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#SignedProperties-Signature-17044">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>sHX8p3YfyvxNIwL2NC54sZdYfvsfXvfYTCks/8BMQlQ=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="SignatureValue-17044">B9Mb3Ogm1QNkpu1N8o4rF0EP4a/vM9Yjgk7EiYZBQmU/r6+yZ/pVC27XYJubbcoxhlth1pk4y15d95I7RfqdqOwMt8dj6P9FKljPH2jCRU0IlHCul3eudjaBWfbu2+fjpyIFRO53kHhOfumkstMKtSCAPSgHRV0wLb06JcZhVCUXUNy+rJfElxzJjhNq+aHcIjayf2TVs1w/elFOz9Ax2v18xol6BRyHnlrQEUtD57AiVurkkcGs0oUqrDGV7IN3YebBJWge/ttXF+5Bz9NnBHQD4iU5ul3aGhcOwq7FY7qxIbmqBHhmcLi7ZGVCXfiEU58Sn6Sa8yBjOILh5Wgzng==</ds:SignatureValue>
<ds:KeyInfo Id="KeyInfoId-Signature-17044">
<ds:X509Data>
<ds:X509Certificate>MIIFRjCCAy6gAwIBAgIGAWZEbfKCMA0GCSqGSIb3DQEBCwUAMG4xCzAJBgNVBAYTAkNSMSkwJwYDVQQKDCBNSU5JU1RFUklPIERFIEhBQ0lFTkRBIC0gU0FOREJPWDEMMAoGA1UECwwD</ds:X509Certificate>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>pVc+nKRrS6q2O05DEO7r1smiY/0PDTZpYSirjEzPaxQ2hRvZDGaxX8wEzqiAE7i9icZbqtwaQPECBRhlzjEK4lxbDhESqYI34CKiMMeARquVGn9WJ/EQjQhGm1KgqROvizX3LRcxDgJzd6pbKPxqTUC/gDAy/PEJJuD9WOH0NVYCREZtD3ShkiIt3DphbDK84Whqhvt47ZyFRSIgjxrRkerhOZj3EzFjr4aIADXUXY2LJGTyKHEe8WcvA9k/aaZGZaTRINOwgk4KMSzNXBDweoxXzN0bJukbxXEicLqO7cJHqoaZX0gCDU8KfAN2UAtmhQ/IRgkSD6O2F72kC86ePQ==</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
<ds:Object Id="XadesObjectId-41822">
<xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" Id="QualifyingProperties-01611" Target="#Signature-17044">
<xades:SignedProperties Id="SignedProperties-Signature-17044">
<xades:SignedSignatureProperties>
<xades:SigningTime>2018-11-12T15:24:21-06:00</xades:SigningTime>
<xades:SigningCertificate>
<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>gVmykfOS2A4p8ALJ6XVLcdTagMz+hMG4ikjzFipgymI=</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>CN=CA PERSONA JURIDICA - SANDBOX,OU=DGT,O=MINISTERIO DE HACIENDA - SANDBOX,C=CR</ds:X509IssuerName>
<ds:X509SerialNumber>1538746348162</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>
</xades:SigningCertificate>
<xades:SignaturePolicyIdentifier>
<xades:SignaturePolicyId>
<xades:SigPolicyId>
<xades:Identifier>https://www.hacienda.go.cr/ATV/ComprobanteElectronico/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf</xades:Identifier>
<xades:Description/>
</xades:SigPolicyId>
<xades:SigPolicyHash>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>a5aV6NckKzC/0CR4tQeTg2ULnhUNK2uDxsO5VuRInTE=</ds:DigestValue>
</xades:SigPolicyHash>
</xades:SignaturePolicyId>
</xades:SignaturePolicyIdentifier>
</xades:SignedSignatureProperties>
<xades:SignedDataObjectProperties>
<xades:DataObjectFormat ObjectReference="#Reference-74608">
<xades:MimeType>text/xml</xades:MimeType>
</xades:DataObjectFormat>
</xades:SignedDataObjectProperties>
</xades:SignedProperties>
</xades:QualifyingProperties>
</ds:Object>
</ds:Signature>
Xml result has the i need but i get alway invalida signature response. While checking the signature this is the response i get
Checking signature...
xmlsec:
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=250:obj=sha256:subj=unknown:error=12:invalid data:data and digest do not match
FAIL
SignedInfo References (ok/all): 1/2
Manifests References (ok/all): 0/0
but i have no idea what the problem can be
Any ideas?
NOTE: CERTIFICATE INFORMATION HAS BEEN CUTED
python xml signature
add a comment |
I'm trying to add a digital signature for an electronic invoice in my country. Signature use XADES-EPS as standard.
I'm using xmlsig and etree to create the signtaure sintax in python 3, like this
def sign_file(cert, password, xml_firma):
min = 1
max = 99999
xmlns_uris = {'ds': 'http://myhost.com/p.xsd'}
random_val = random.randint(min, max)
signature_id = 'Signature-' + str(random_val)
#signed_properties_id = signature_id + '-SignedProperties%05d'
# % random.randint(min, max)
signed_properties_id = 'SignedProperties-' + signature_id
signature_value = 'SignatureValue-' + str(random_val)
qualifying_properties = 'QualifyingProperties-%05d' % random.randint(min, max)
#key_info_id = 'KeyInfo%05d' % random.randint(min, max)
key_info_id = 'KeyInfoId-' + signature_id
reference_id = 'Reference-%05d' % random.randint(min, max)
#object_id = 'Object%05d' % random.randint(min, max)
object_id = 'XadesObjectId-%05d' % random.randint(min, max)
xades = 'http://uri.etsi.org/01903/v1.3.2#'
ds = 'http://www.w3.org/2000/09/xmldsig#'
xades141 = 'http://uri.etsi.org/01903/v1.4.1#'
sig_policy_identifier = 'https://www.hacienda.go.cr/ATV/ComprobanteElectronico/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf'
sig_policy_hash_value = 'V8lVVNGDCPen6VELRD1Ja8HARFk='
xml_firmar = base64decode(xml_firma)
root = etree.fromstring(xml_firmar)
certificate = crypto.load_pkcs12(base64.b64decode(cert), password)
# GENERAR NODO PERSONALIZADO PARA FE
sign = etree.Element(
etree.QName(ds, 'Signature'),
nsmap={'ds': ds},
attrib={
xmlsig.constants.ID_ATTR: signature_id,
}
)
#annotate_with_xmlns_prefixes(sign, 'ds')
#add_xmnls_attributes(sign, xmlns_uris)
# GENERO EL NODO ds:SignedInfo
signed_info = etree.SubElement(
sign,
etree.QName(ds, 'SignedInfo')
)
# CREO EL NODO ds:CanonicalizationMethod DENTRO DE signed_info
etree.SubElement(
signed_info,
etree.QName(ds, 'CanonicalizationMethod'),
attrib={
'Algorithm': xmlsig.constants.TransformInclC14N
}
)
# CREO EL NODO ds:SignatureMethod DENTRO DE signed_info
etree.SubElement(
signed_info,
etree.QName(ds, 'SignatureMethod'),
attrib={
'Algorithm': xmlsig.constants.TransformRsaSha256
}
)
# CREO EL NODO ds:SignatureMethod DENTRO DE signed_info
reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
xmlsig.constants.ID_ATTR: reference_id,
'URI': ''
}
)
# CREO EL NODO ds:Transforms DENTRO DE reference
transforms = etree.SubElement(
reference,
etree.QName(ds, 'Transforms'),
)
# CREO EL NODO ds:Transform DENTRO DE trasnforms
etree.SubElement(
transforms,
etree.QName(ds, 'Transform'),
attrib={
'Algorithm': 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value.digest())
# CREO EL SEGUNDO NODO ds:SignatureMethod DENTRO DE signed_info
sec_reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
xmlsig.constants.ID_ATTR: 'ReferenceKeyInfo',
'URI': '#' + key_info_id
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
sec_reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value2 = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
sec_reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value2.digest())
# CREO EL TERCER NODO ds:Reference DENTRO DE signed_info
tr_reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
'Type': 'http://uri.etsi.org/01903#SignedProperties',
'URI': '#' + signed_properties_id,
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
tr_reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value3 = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
tr_reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value3.digest())
# GENERO EL NODO ds:SignatureValue
etree.SubElement(
sign,
etree.QName(ds, 'SignatureValue'),
attrib={
xmlsig.constants.ID_ATTR: signature_value
}
)
# GENERO EL NODO ds:KeyInfo
key_info = etree.SubElement(
sign,
etree.QName(ds, 'KeyInfo'),
attrib={
xmlsig.constants.ID_ATTR: key_info_id
}
)
# GENERO EL NODO ds:X509Data
x509 = etree.SubElement(
key_info,
etree.QName(ds, 'X509Data'),
)
# GENERO EL NODO ds:X509Certificate
etree.SubElement(
x509,
etree.QName(ds, 'X509Certificate'),
)
# GENERO EL NODO ds:KeyValue
etree.SubElement(
key_info,
etree.QName(ds, 'KeyValue'),
)
#AQUI EMPIEZO A CREAR EL NODO DE QUALIFYNG PROPERTIES
object_node = etree.SubElement(
sign,
etree.QName(xmlsig.constants.DSigNs, 'Object'),
#nsmap={'etsi': etsi},
attrib={xmlsig.constants.ID_ATTR: object_id}
#nsmap={'xades': xades},
#attrib={xmlsig.constants.ID_ATTR: object_id} NO SE NECESITA EN EL XML DE HACIENDA
)
#CREO EL SUBNODO QUALIFYING PROPERTIES
qualifying_properties = etree.SubElement(
object_node,
etree.QName(xades, 'QualifyingProperties'),
nsmap = {'xades': xades, 'xades141': xades141},
attrib={
xmlsig.constants.ID_ATTR: qualifying_properties,
'Target': '#' + signature_id
})
#CREO EL NODO xades:SignedProperties DENTRO DE QUALIFYING PROPERTIES
signed_properties = etree.SubElement(
qualifying_properties,
etree.QName(xades, 'SignedProperties'),
attrib={
xmlsig.constants.ID_ATTR: signed_properties_id #ESTO HAY QUE CAMBIARLO PARA QUE SEA COMO LO PIDE HACIENDA
}
)
# CREO EL NODO xades:SignedSignatureProperties DENTRO DE SIGNED PROPERTIES
signed_signature_properties = etree.SubElement(
signed_properties,
etree.QName(xades, 'SignedSignatureProperties')
)
#GENERO LA HORA PARA EL NODO xades:SigningTime
#now = datetime.datetime.now().replace(
# microsecond=0, tzinfo=pytz.utc
#)
# GENERO EL NODO xades:SigningTime Y LE PONGO LA HORA
etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SigningTime')
).text = get_time_hacienda()
#GENERO EL NODO xades:SigningCertificate
signing_certificate = etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SigningCertificate')
)
#GENERO EL NODO xades:Cert DENTRO DE xades:SigningCertificate
signing_certificate_cert = etree.SubElement(
signing_certificate,
etree.QName(xades, 'Cert')
)
#GENERO EL NODO xades:CertDigest DENTRO DE xades:cert
cert_digest = etree.SubElement(
signing_certificate_cert,
etree.QName(xades, 'CertDigest')
)
#GENERO EL NODO ds:DigestMethod DENTRO DE xades:CertDigest
etree.SubElement(
cert_digest,
etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
hash_cert = hashlib.sha256(
crypto.dump_certificate(
crypto.FILETYPE_ASN1,
certificate.get_certificate()
)
)
# GENERO EL NODO ds:DigestValue DENTRO DE xades:CertDigest Y LE PONGO EL VALOR DEL DIGESTVALUE ANTERIOR
etree.SubElement(
cert_digest,
etree.QName(xmlsig.constants.DSigNs, 'DigestValue')
).text = base64.b64encode(hash_cert.digest())
# GENERO EL NODO xades:IssuerSerial DENTRO DE xades:Cert
issuer_serial = etree.SubElement(
signing_certificate_cert,
etree.QName(xades, 'IssuerSerial')
)
# GENERO EL NODO ds:X509IssuerName DENTRO DE xades:IssuerSerial
etree.SubElement(
issuer_serial,
etree.QName(xmlsig.constants.DSigNs, 'X509IssuerName')
).text = xmlsig.utils.get_rdns_name(certificate.get_certificate().to_cryptography().issuer.rdns)
# GENERO EL NODO ds:X509SerialNumber DENTRO DE xades:IssuerSerial
etree.SubElement(
issuer_serial,
etree.QName(xmlsig.constants.DSigNs, 'X509SerialNumber')
).text = str(certificate.get_certificate().get_serial_number())
# GENERO EL NODO xades:SignaturePolicyIdentifier DENTRO DE sign
signature_policy_identifier = etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SignaturePolicyIdentifier')
)
# GENERO EL NODO xades:SignaturePolicyId DENTRO DE xades:SignaturePolicyIdentifier
signature_policy_id = etree.SubElement(
signature_policy_identifier,
etree.QName(xades, 'SignaturePolicyId')
)
# GENERO EL NODO xades:SigPolicyId DENTRO DE xades:SignaturePolicyId
sig_policy_id = etree.SubElement(
signature_policy_id,
etree.QName(xades, 'SigPolicyId')
)
# GENERO EL NODO xades:Identifier DENTRO DE xades:SigPolicyId
etree.SubElement(
sig_policy_id,
etree.QName(xades, 'Identifier')
).text = sig_policy_identifier
#BORRO ESTE NODO PUES EN FE COSTA RICA NO SE NECESITA
etree.SubElement(
sig_policy_id,
etree.QName(xades, 'Description')
)#.text = "Política de Firma FacturaE v3.1"
# GENERO EL NODO xades:Identifier DENTRO DE signature_policy_id
sig_policy_hash = etree.SubElement(
signature_policy_id,
etree.QName(xades, 'SigPolicyHash')
)
# GENERO EL NODO ds:DigestMethod DENTRO DE xades:Identifier
etree.SubElement(
sig_policy_hash,
etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1'
})
#GENERO EL DIGEST PARA EL CERTIFICADO LEYENDOLO DE LA URL DE HACIENDA
try:
remote = urllib.request.urlopen(sig_policy_identifier)
hash_value = base64.b64encode(hashlib.sha256(remote.read()).digest())
except urllib.request.HTTPError:
hash_value = sig_policy_hash_value
# GENERO EL NODO ds:DigestValue ds:DigestMethod DENTRO DE sig_policy_hash
etree.SubElement(
sig_policy_hash,
etree.QName(xmlsig.constants.DSigNs, 'DigestValue')
).text = hash_value
#NO REQUERIDO PARA FE COSTA RICA
#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(xades, 'SignedDataObjectProperties')
)
data_object_format = etree.SubElement(
signed_data_object_properties,
etree.QName(xades, 'DataObjectFormat'),
attrib={
'ObjectReference': '#' + reference_id
}
)
#etree.SubElement(
# data_object_format,
# etree.QName(etsi, 'Description')
#).text = 'Factura'
etree.SubElement(
data_object_format,
etree.QName(xades, '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)
root.xpath(
'//ds:Signature', namespaces={'ds': xmlsig.constants.DSigNs}
)[0]
ctx.sign(sign)
#xml_bytes = etree.tostring(root, xml_declaration=True)
#return stringToBase64(xml_bytes)
return etree.tostring(
root
)
That code produces a xml signature in the following way
<ds:Signature Id="Signature-17044">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference Id="Reference-74608" URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>+fhrrmGMWpvy8IEowaa2fLh905rLv6xRmKB4zD6Omtg=</ds:DigestValue>
</ds:Reference>
<ds:Reference Id="ReferenceKeyInfo" URI="#KeyInfoId-Signature-17044">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>zXjLpycv3XvGI47RJR0qIoxtZoApAHwkdjDqRKiGpTU=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#SignedProperties-Signature-17044">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>sHX8p3YfyvxNIwL2NC54sZdYfvsfXvfYTCks/8BMQlQ=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="SignatureValue-17044">B9Mb3Ogm1QNkpu1N8o4rF0EP4a/vM9Yjgk7EiYZBQmU/r6+yZ/pVC27XYJubbcoxhlth1pk4y15d95I7RfqdqOwMt8dj6P9FKljPH2jCRU0IlHCul3eudjaBWfbu2+fjpyIFRO53kHhOfumkstMKtSCAPSgHRV0wLb06JcZhVCUXUNy+rJfElxzJjhNq+aHcIjayf2TVs1w/elFOz9Ax2v18xol6BRyHnlrQEUtD57AiVurkkcGs0oUqrDGV7IN3YebBJWge/ttXF+5Bz9NnBHQD4iU5ul3aGhcOwq7FY7qxIbmqBHhmcLi7ZGVCXfiEU58Sn6Sa8yBjOILh5Wgzng==</ds:SignatureValue>
<ds:KeyInfo Id="KeyInfoId-Signature-17044">
<ds:X509Data>
<ds:X509Certificate>MIIFRjCCAy6gAwIBAgIGAWZEbfKCMA0GCSqGSIb3DQEBCwUAMG4xCzAJBgNVBAYTAkNSMSkwJwYDVQQKDCBNSU5JU1RFUklPIERFIEhBQ0lFTkRBIC0gU0FOREJPWDEMMAoGA1UECwwD</ds:X509Certificate>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>pVc+nKRrS6q2O05DEO7r1smiY/0PDTZpYSirjEzPaxQ2hRvZDGaxX8wEzqiAE7i9icZbqtwaQPECBRhlzjEK4lxbDhESqYI34CKiMMeARquVGn9WJ/EQjQhGm1KgqROvizX3LRcxDgJzd6pbKPxqTUC/gDAy/PEJJuD9WOH0NVYCREZtD3ShkiIt3DphbDK84Whqhvt47ZyFRSIgjxrRkerhOZj3EzFjr4aIADXUXY2LJGTyKHEe8WcvA9k/aaZGZaTRINOwgk4KMSzNXBDweoxXzN0bJukbxXEicLqO7cJHqoaZX0gCDU8KfAN2UAtmhQ/IRgkSD6O2F72kC86ePQ==</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
<ds:Object Id="XadesObjectId-41822">
<xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" Id="QualifyingProperties-01611" Target="#Signature-17044">
<xades:SignedProperties Id="SignedProperties-Signature-17044">
<xades:SignedSignatureProperties>
<xades:SigningTime>2018-11-12T15:24:21-06:00</xades:SigningTime>
<xades:SigningCertificate>
<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>gVmykfOS2A4p8ALJ6XVLcdTagMz+hMG4ikjzFipgymI=</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>CN=CA PERSONA JURIDICA - SANDBOX,OU=DGT,O=MINISTERIO DE HACIENDA - SANDBOX,C=CR</ds:X509IssuerName>
<ds:X509SerialNumber>1538746348162</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>
</xades:SigningCertificate>
<xades:SignaturePolicyIdentifier>
<xades:SignaturePolicyId>
<xades:SigPolicyId>
<xades:Identifier>https://www.hacienda.go.cr/ATV/ComprobanteElectronico/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf</xades:Identifier>
<xades:Description/>
</xades:SigPolicyId>
<xades:SigPolicyHash>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>a5aV6NckKzC/0CR4tQeTg2ULnhUNK2uDxsO5VuRInTE=</ds:DigestValue>
</xades:SigPolicyHash>
</xades:SignaturePolicyId>
</xades:SignaturePolicyIdentifier>
</xades:SignedSignatureProperties>
<xades:SignedDataObjectProperties>
<xades:DataObjectFormat ObjectReference="#Reference-74608">
<xades:MimeType>text/xml</xades:MimeType>
</xades:DataObjectFormat>
</xades:SignedDataObjectProperties>
</xades:SignedProperties>
</xades:QualifyingProperties>
</ds:Object>
</ds:Signature>
Xml result has the i need but i get alway invalida signature response. While checking the signature this is the response i get
Checking signature...
xmlsec:
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=250:obj=sha256:subj=unknown:error=12:invalid data:data and digest do not match
FAIL
SignedInfo References (ok/all): 1/2
Manifests References (ok/all): 0/0
but i have no idea what the problem can be
Any ideas?
NOTE: CERTIFICATE INFORMATION HAS BEEN CUTED
python xml signature
I'm trying to add a digital signature for an electronic invoice in my country. Signature use XADES-EPS as standard.
I'm using xmlsig and etree to create the signtaure sintax in python 3, like this
def sign_file(cert, password, xml_firma):
min = 1
max = 99999
xmlns_uris = {'ds': 'http://myhost.com/p.xsd'}
random_val = random.randint(min, max)
signature_id = 'Signature-' + str(random_val)
#signed_properties_id = signature_id + '-SignedProperties%05d'
# % random.randint(min, max)
signed_properties_id = 'SignedProperties-' + signature_id
signature_value = 'SignatureValue-' + str(random_val)
qualifying_properties = 'QualifyingProperties-%05d' % random.randint(min, max)
#key_info_id = 'KeyInfo%05d' % random.randint(min, max)
key_info_id = 'KeyInfoId-' + signature_id
reference_id = 'Reference-%05d' % random.randint(min, max)
#object_id = 'Object%05d' % random.randint(min, max)
object_id = 'XadesObjectId-%05d' % random.randint(min, max)
xades = 'http://uri.etsi.org/01903/v1.3.2#'
ds = 'http://www.w3.org/2000/09/xmldsig#'
xades141 = 'http://uri.etsi.org/01903/v1.4.1#'
sig_policy_identifier = 'https://www.hacienda.go.cr/ATV/ComprobanteElectronico/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf'
sig_policy_hash_value = 'V8lVVNGDCPen6VELRD1Ja8HARFk='
xml_firmar = base64decode(xml_firma)
root = etree.fromstring(xml_firmar)
certificate = crypto.load_pkcs12(base64.b64decode(cert), password)
# GENERAR NODO PERSONALIZADO PARA FE
sign = etree.Element(
etree.QName(ds, 'Signature'),
nsmap={'ds': ds},
attrib={
xmlsig.constants.ID_ATTR: signature_id,
}
)
#annotate_with_xmlns_prefixes(sign, 'ds')
#add_xmnls_attributes(sign, xmlns_uris)
# GENERO EL NODO ds:SignedInfo
signed_info = etree.SubElement(
sign,
etree.QName(ds, 'SignedInfo')
)
# CREO EL NODO ds:CanonicalizationMethod DENTRO DE signed_info
etree.SubElement(
signed_info,
etree.QName(ds, 'CanonicalizationMethod'),
attrib={
'Algorithm': xmlsig.constants.TransformInclC14N
}
)
# CREO EL NODO ds:SignatureMethod DENTRO DE signed_info
etree.SubElement(
signed_info,
etree.QName(ds, 'SignatureMethod'),
attrib={
'Algorithm': xmlsig.constants.TransformRsaSha256
}
)
# CREO EL NODO ds:SignatureMethod DENTRO DE signed_info
reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
xmlsig.constants.ID_ATTR: reference_id,
'URI': ''
}
)
# CREO EL NODO ds:Transforms DENTRO DE reference
transforms = etree.SubElement(
reference,
etree.QName(ds, 'Transforms'),
)
# CREO EL NODO ds:Transform DENTRO DE trasnforms
etree.SubElement(
transforms,
etree.QName(ds, 'Transform'),
attrib={
'Algorithm': 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value.digest())
# CREO EL SEGUNDO NODO ds:SignatureMethod DENTRO DE signed_info
sec_reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
xmlsig.constants.ID_ATTR: 'ReferenceKeyInfo',
'URI': '#' + key_info_id
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
sec_reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value2 = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
sec_reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value2.digest())
# CREO EL TERCER NODO ds:Reference DENTRO DE signed_info
tr_reference = etree.SubElement(
signed_info,
etree.QName(ds, 'Reference'),
attrib={
'Type': 'http://uri.etsi.org/01903#SignedProperties',
'URI': '#' + signed_properties_id,
}
)
# CREO EL NODO ds:DigestMethod DENTRO DE reference
etree.SubElement(
tr_reference,
etree.QName(ds, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
#digest_value3 = hashlib.sha256(
# crypto.dump_certificate(
# crypto.FILETYPE_ASN1,
# certificate.get_certificate()
# )
#)
# GENERO EL NODO ds:DigestValue DENTRO DE reference
etree.SubElement(
tr_reference,
etree.QName(ds, 'DigestValue')
)#.text = base64.b64encode(digest_value3.digest())
# GENERO EL NODO ds:SignatureValue
etree.SubElement(
sign,
etree.QName(ds, 'SignatureValue'),
attrib={
xmlsig.constants.ID_ATTR: signature_value
}
)
# GENERO EL NODO ds:KeyInfo
key_info = etree.SubElement(
sign,
etree.QName(ds, 'KeyInfo'),
attrib={
xmlsig.constants.ID_ATTR: key_info_id
}
)
# GENERO EL NODO ds:X509Data
x509 = etree.SubElement(
key_info,
etree.QName(ds, 'X509Data'),
)
# GENERO EL NODO ds:X509Certificate
etree.SubElement(
x509,
etree.QName(ds, 'X509Certificate'),
)
# GENERO EL NODO ds:KeyValue
etree.SubElement(
key_info,
etree.QName(ds, 'KeyValue'),
)
#AQUI EMPIEZO A CREAR EL NODO DE QUALIFYNG PROPERTIES
object_node = etree.SubElement(
sign,
etree.QName(xmlsig.constants.DSigNs, 'Object'),
#nsmap={'etsi': etsi},
attrib={xmlsig.constants.ID_ATTR: object_id}
#nsmap={'xades': xades},
#attrib={xmlsig.constants.ID_ATTR: object_id} NO SE NECESITA EN EL XML DE HACIENDA
)
#CREO EL SUBNODO QUALIFYING PROPERTIES
qualifying_properties = etree.SubElement(
object_node,
etree.QName(xades, 'QualifyingProperties'),
nsmap = {'xades': xades, 'xades141': xades141},
attrib={
xmlsig.constants.ID_ATTR: qualifying_properties,
'Target': '#' + signature_id
})
#CREO EL NODO xades:SignedProperties DENTRO DE QUALIFYING PROPERTIES
signed_properties = etree.SubElement(
qualifying_properties,
etree.QName(xades, 'SignedProperties'),
attrib={
xmlsig.constants.ID_ATTR: signed_properties_id #ESTO HAY QUE CAMBIARLO PARA QUE SEA COMO LO PIDE HACIENDA
}
)
# CREO EL NODO xades:SignedSignatureProperties DENTRO DE SIGNED PROPERTIES
signed_signature_properties = etree.SubElement(
signed_properties,
etree.QName(xades, 'SignedSignatureProperties')
)
#GENERO LA HORA PARA EL NODO xades:SigningTime
#now = datetime.datetime.now().replace(
# microsecond=0, tzinfo=pytz.utc
#)
# GENERO EL NODO xades:SigningTime Y LE PONGO LA HORA
etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SigningTime')
).text = get_time_hacienda()
#GENERO EL NODO xades:SigningCertificate
signing_certificate = etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SigningCertificate')
)
#GENERO EL NODO xades:Cert DENTRO DE xades:SigningCertificate
signing_certificate_cert = etree.SubElement(
signing_certificate,
etree.QName(xades, 'Cert')
)
#GENERO EL NODO xades:CertDigest DENTRO DE xades:cert
cert_digest = etree.SubElement(
signing_certificate_cert,
etree.QName(xades, 'CertDigest')
)
#GENERO EL NODO ds:DigestMethod DENTRO DE xades:CertDigest
etree.SubElement(
cert_digest,
etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2001/04/xmlenc#sha256'
}
)
# OBTENGO EL DIGEST VALUE DEL CERTIFICADO PARA EL NODO DIGESTVALUE
hash_cert = hashlib.sha256(
crypto.dump_certificate(
crypto.FILETYPE_ASN1,
certificate.get_certificate()
)
)
# GENERO EL NODO ds:DigestValue DENTRO DE xades:CertDigest Y LE PONGO EL VALOR DEL DIGESTVALUE ANTERIOR
etree.SubElement(
cert_digest,
etree.QName(xmlsig.constants.DSigNs, 'DigestValue')
).text = base64.b64encode(hash_cert.digest())
# GENERO EL NODO xades:IssuerSerial DENTRO DE xades:Cert
issuer_serial = etree.SubElement(
signing_certificate_cert,
etree.QName(xades, 'IssuerSerial')
)
# GENERO EL NODO ds:X509IssuerName DENTRO DE xades:IssuerSerial
etree.SubElement(
issuer_serial,
etree.QName(xmlsig.constants.DSigNs, 'X509IssuerName')
).text = xmlsig.utils.get_rdns_name(certificate.get_certificate().to_cryptography().issuer.rdns)
# GENERO EL NODO ds:X509SerialNumber DENTRO DE xades:IssuerSerial
etree.SubElement(
issuer_serial,
etree.QName(xmlsig.constants.DSigNs, 'X509SerialNumber')
).text = str(certificate.get_certificate().get_serial_number())
# GENERO EL NODO xades:SignaturePolicyIdentifier DENTRO DE sign
signature_policy_identifier = etree.SubElement(
signed_signature_properties,
etree.QName(xades, 'SignaturePolicyIdentifier')
)
# GENERO EL NODO xades:SignaturePolicyId DENTRO DE xades:SignaturePolicyIdentifier
signature_policy_id = etree.SubElement(
signature_policy_identifier,
etree.QName(xades, 'SignaturePolicyId')
)
# GENERO EL NODO xades:SigPolicyId DENTRO DE xades:SignaturePolicyId
sig_policy_id = etree.SubElement(
signature_policy_id,
etree.QName(xades, 'SigPolicyId')
)
# GENERO EL NODO xades:Identifier DENTRO DE xades:SigPolicyId
etree.SubElement(
sig_policy_id,
etree.QName(xades, 'Identifier')
).text = sig_policy_identifier
#BORRO ESTE NODO PUES EN FE COSTA RICA NO SE NECESITA
etree.SubElement(
sig_policy_id,
etree.QName(xades, 'Description')
)#.text = "Política de Firma FacturaE v3.1"
# GENERO EL NODO xades:Identifier DENTRO DE signature_policy_id
sig_policy_hash = etree.SubElement(
signature_policy_id,
etree.QName(xades, 'SigPolicyHash')
)
# GENERO EL NODO ds:DigestMethod DENTRO DE xades:Identifier
etree.SubElement(
sig_policy_hash,
etree.QName(xmlsig.constants.DSigNs, 'DigestMethod'),
attrib={
'Algorithm': 'http://www.w3.org/2000/09/xmldsig#sha1'
})
#GENERO EL DIGEST PARA EL CERTIFICADO LEYENDOLO DE LA URL DE HACIENDA
try:
remote = urllib.request.urlopen(sig_policy_identifier)
hash_value = base64.b64encode(hashlib.sha256(remote.read()).digest())
except urllib.request.HTTPError:
hash_value = sig_policy_hash_value
# GENERO EL NODO ds:DigestValue ds:DigestMethod DENTRO DE sig_policy_hash
etree.SubElement(
sig_policy_hash,
etree.QName(xmlsig.constants.DSigNs, 'DigestValue')
).text = hash_value
#NO REQUERIDO PARA FE COSTA RICA
#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(xades, 'SignedDataObjectProperties')
)
data_object_format = etree.SubElement(
signed_data_object_properties,
etree.QName(xades, 'DataObjectFormat'),
attrib={
'ObjectReference': '#' + reference_id
}
)
#etree.SubElement(
# data_object_format,
# etree.QName(etsi, 'Description')
#).text = 'Factura'
etree.SubElement(
data_object_format,
etree.QName(xades, '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)
root.xpath(
'//ds:Signature', namespaces={'ds': xmlsig.constants.DSigNs}
)[0]
ctx.sign(sign)
#xml_bytes = etree.tostring(root, xml_declaration=True)
#return stringToBase64(xml_bytes)
return etree.tostring(
root
)
That code produces a xml signature in the following way
<ds:Signature Id="Signature-17044">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference Id="Reference-74608" URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>+fhrrmGMWpvy8IEowaa2fLh905rLv6xRmKB4zD6Omtg=</ds:DigestValue>
</ds:Reference>
<ds:Reference Id="ReferenceKeyInfo" URI="#KeyInfoId-Signature-17044">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>zXjLpycv3XvGI47RJR0qIoxtZoApAHwkdjDqRKiGpTU=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#SignedProperties-Signature-17044">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>sHX8p3YfyvxNIwL2NC54sZdYfvsfXvfYTCks/8BMQlQ=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="SignatureValue-17044">B9Mb3Ogm1QNkpu1N8o4rF0EP4a/vM9Yjgk7EiYZBQmU/r6+yZ/pVC27XYJubbcoxhlth1pk4y15d95I7RfqdqOwMt8dj6P9FKljPH2jCRU0IlHCul3eudjaBWfbu2+fjpyIFRO53kHhOfumkstMKtSCAPSgHRV0wLb06JcZhVCUXUNy+rJfElxzJjhNq+aHcIjayf2TVs1w/elFOz9Ax2v18xol6BRyHnlrQEUtD57AiVurkkcGs0oUqrDGV7IN3YebBJWge/ttXF+5Bz9NnBHQD4iU5ul3aGhcOwq7FY7qxIbmqBHhmcLi7ZGVCXfiEU58Sn6Sa8yBjOILh5Wgzng==</ds:SignatureValue>
<ds:KeyInfo Id="KeyInfoId-Signature-17044">
<ds:X509Data>
<ds:X509Certificate>MIIFRjCCAy6gAwIBAgIGAWZEbfKCMA0GCSqGSIb3DQEBCwUAMG4xCzAJBgNVBAYTAkNSMSkwJwYDVQQKDCBNSU5JU1RFUklPIERFIEhBQ0lFTkRBIC0gU0FOREJPWDEMMAoGA1UECwwD</ds:X509Certificate>
</ds:X509Data>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>pVc+nKRrS6q2O05DEO7r1smiY/0PDTZpYSirjEzPaxQ2hRvZDGaxX8wEzqiAE7i9icZbqtwaQPECBRhlzjEK4lxbDhESqYI34CKiMMeARquVGn9WJ/EQjQhGm1KgqROvizX3LRcxDgJzd6pbKPxqTUC/gDAy/PEJJuD9WOH0NVYCREZtD3ShkiIt3DphbDK84Whqhvt47ZyFRSIgjxrRkerhOZj3EzFjr4aIADXUXY2LJGTyKHEe8WcvA9k/aaZGZaTRINOwgk4KMSzNXBDweoxXzN0bJukbxXEicLqO7cJHqoaZX0gCDU8KfAN2UAtmhQ/IRgkSD6O2F72kC86ePQ==</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
<ds:Object Id="XadesObjectId-41822">
<xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" Id="QualifyingProperties-01611" Target="#Signature-17044">
<xades:SignedProperties Id="SignedProperties-Signature-17044">
<xades:SignedSignatureProperties>
<xades:SigningTime>2018-11-12T15:24:21-06:00</xades:SigningTime>
<xades:SigningCertificate>
<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>gVmykfOS2A4p8ALJ6XVLcdTagMz+hMG4ikjzFipgymI=</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>CN=CA PERSONA JURIDICA - SANDBOX,OU=DGT,O=MINISTERIO DE HACIENDA - SANDBOX,C=CR</ds:X509IssuerName>
<ds:X509SerialNumber>1538746348162</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>
</xades:SigningCertificate>
<xades:SignaturePolicyIdentifier>
<xades:SignaturePolicyId>
<xades:SigPolicyId>
<xades:Identifier>https://www.hacienda.go.cr/ATV/ComprobanteElectronico/docs/esquemas/2016/v4/Resolucion%20Comprobantes%20Electronicos%20%20DGT-R-48-2016.pdf</xades:Identifier>
<xades:Description/>
</xades:SigPolicyId>
<xades:SigPolicyHash>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>a5aV6NckKzC/0CR4tQeTg2ULnhUNK2uDxsO5VuRInTE=</ds:DigestValue>
</xades:SigPolicyHash>
</xades:SignaturePolicyId>
</xades:SignaturePolicyIdentifier>
</xades:SignedSignatureProperties>
<xades:SignedDataObjectProperties>
<xades:DataObjectFormat ObjectReference="#Reference-74608">
<xades:MimeType>text/xml</xades:MimeType>
</xades:DataObjectFormat>
</xades:SignedDataObjectProperties>
</xades:SignedProperties>
</xades:QualifyingProperties>
</ds:Object>
</ds:Signature>
Xml result has the i need but i get alway invalida signature response. While checking the signature this is the response i get
Checking signature...
xmlsec:
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=250:obj=sha256:subj=unknown:error=12:invalid data:data and digest do not match
FAIL
SignedInfo References (ok/all): 1/2
Manifests References (ok/all): 0/0
but i have no idea what the problem can be
Any ideas?
NOTE: CERTIFICATE INFORMATION HAS BEEN CUTED
python xml signature
python xml signature
asked Nov 12 '18 at 21:30
Jason
2516
2516
add a comment |
add a comment |
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270392%2fxml-digital-signature-in-python%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270392%2fxml-digital-signature-in-python%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown