sorry for my english firstly. I'm taking my first steps with signxml in Python, and from the articles I've seen it looks quite easy to use, but I haven't been able to correctly sign the document as I need it.
I was looking at the documentation and some articles on the web, and among the problems I was having when signing a document with signxml, I found an article at https://technotes.shemyak.com/posts/xml-signatures-with-python-elementtree/. So I tried to use my data with this structure.
ET.register_namespace("ds", "http://www.w3.org/2000/09/xmldsig#")
xml_con_semilla = ET.fromstring("<getToken><item><Semilla>000009574333</Semilla></item><ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" Id=\"placeholder\"></ds:Signature></getToken>")
Here they explain some of the difficulties at the time of signing and verification. Using the method on this website I got the correct signature and verification even of the serialized data at the end of the steps, but when I send the xml to the service I need, it gives me problems. I suppose it must be because it does not have the structure that is requested.
The structure that the service is waiting for is the following
<?xml version="1.0"?>
<getToken>
<item>
<Semilla>000009574333</Semilla>
</item>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>XX...</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>XX...</SignatureValue>
<KeyInfo>
<KeyValue>
<RSAKeyValue>
<Modulus>XX...</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
<X509Data>
<X509Certificate>XX...</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</getToken>
So I'm doing a clean try, from an XML generated with etree, since I think the above solution doesn't satisfy what I need.
import xml.etree.ElementTree as ET
import signxml
semilla = '000009574333'
xml_semilla = ET.Element('getToken')
item = ET.SubElement(xml_semilla, 'item')
ET.SubElement(item, 'Semilla').text = semilla
>>> <getToken><item><Semilla>000009574333</Semilla></item></getToken>
semilla_xml_firmada = FirmarXMLSemilla(xml_semilla)
def FirmarXMLSemilla(xml_semilla):
pem_cert, pem_key = [open(f, "rb").read() for f in ("cert.pem", "key.pem")]
signer = XMLSigner(method=signxml.methods.enveloped,
signature_algorithm='rsa-sha1',
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315',
digest_algorithm="sha1")
signed_root = signer.sign(xml_semilla, key=pem_key, cert=pem_cert, always_add_key_value=True)
try:
verified_data = XMLVerifier().verify(signed_root, x509_cert=pem_cert)
print('Documento Firmado Correctamente')
except Exception as e:
logging.error('Error de Verificación en la Firma o Respuesta SAML: {}'.format(e))
Up to this point, without serializing, the signature is correct
VerifyResult(signed_data=b'<getToken><item><Semilla>000009574333</Semilla></item></getToken>', signed_xml=<Element getToken at 0x24cd69e65c0>, signature_xml=<Element {http://www.w3.org/2000/09/xmldsig#}Signature at 0x24cd69e6640>)
data_serialized = ET.tostring(signed_root)
data_serialized =
<getToken xmlns:ns0="http://www.w3.org/2000/09/xmldsig#">
<item>
<Semilla>000009574333</Semilla>
</item>
<ns0:Signature>
<ns0:SignedInfo>
<ns0:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<ns0:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<ns0:Reference URI="">
<ns0:Transforms>
<ns0:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</ns0:Transforms>
<ns0:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ns0:DigestValue>XX...</ns0:DigestValue>
</ns0:Reference>
</ns0:SignedInfo>
<ns0:SignatureValue>XX...</ns0:SignatureValue>
<ns0:KeyInfo>
<ns0:KeyValue>
<ns0:RSAKeyValue>
<ns0:Modulus>XX...</ns0:Modulus>
<ns0:Exponent>AQAB</ns0:Exponent>
</ns0:RSAKeyValue>
</ns0:KeyValue>
<ns0:X509Data>
<ns0:X509Certificate>XX...</ns0:X509Certificate>
</ns0:X509Data>
</ns0:KeyInfo>
</ns0:Signature>
</getToken>
# Sending the data...
data_parsed = ET.fromstring(data_serialized)
try:
verified_data = XMLVerifier().verify(data_parsed, x509_cert=pem_cert)
print('Documento Firmado Correctamente')
logging.debug(verified_data)
except Exception as e:
logging.error('Error de Verificación en la Firma o Respuesta SAML: {}'.format(e))
ERROR:root:Error de Verificación en la Firma o Respuesta SAML: Signature verification failed: bad signature
As you will notice, when converting to a string, namespaces are added to the data, which breaks the signature. In addition, the "ns0:" prefixes are added.
I need xmlns="http://www.w3.org/2000/09/xmldsig#" to be added to Signature, not getToken
I also tried Python signxml XML signature package. How to add xml placehoder for Signature tag? but it also gives me problems.
I am using signxml version 3.0.1, so that it will allow me to use SHA-1.
Thanks!
I had the same problem. It seems like the issue is the way you are saving your XML. If you use etree it should do the trick.
also, with the
signer.namespaces = {None: namespaces.ds}you should be able to eliminate namespaces.