1

I'm attempting to use M2Crypto to verify a signature contained in an XML response returned from my SSO/SAML provider in my django/python app, but I can't seem to get it to work.

My XML response looks sort of like the second example here.

ETA: And here's a pastebin of my actual XML.

I'm using some code like this to attempt the verification:

def verify_signature(signed_info, cert, signature):
    from M2Crypto import EVP, RSA, X509

    x509 = X509.load_cert_string(base64.decodestring(cert), X509.FORMAT_DER)
    pubkey = x509.get_pubkey().get_rsa()
    verify_EVP = EVP.PKey()
    verify_EVP.assign_rsa(pubkey)
    verify_EVP.reset_context(md='sha1')
    verify_EVP.verify_init()

    verify_EVP.verify_update(signature.decode('base64'))
    result = verify_EVP.verify_final(signed_info)

    return result

I can successfully get the NameID from the response, and I know I'm successfully loading the certificate, because I can pull the issuer, etc. out of it.

As for the signature, though, I've tried hashing the passed in XML, encoding/not encoding various pieces, and passing in various bits of XML for the signed_info parameter (the SignedInfo tag, the Response tag, the whole thing), and I've tried using ElementTree/ElementC14N.py to ensure the XML is exclusively canonicalized, as the Transform implies should be done, but I'm not getting a positive result.

What am I missing here? Am I trying to validate against the wrong XML? Something wrong with my verification technique?

Ennael
  • 861
  • 3
  • 14
  • 31
  • Did you ever figure this out @Ennael? Would you mind taking a look at [my question](http://stackoverflow.com/questions/21209510/validating-saml-signature-in-python)? – Sindri Guðmundsson Jan 20 '14 at 12:43

2 Answers2

2

You were so close! You should pass to verify_update the signed_info, and then to verify_final pass the signature.

You do need to make sure that your signed_info is correctly canonicalized before verifying the signature.

Here is the correct method:

def verify_signature(signed_info, cert, signature):
    from M2Crypto import EVP, RSA, X509

    x509 = X509.load_cert_string(base64.decodestring(cert), X509.FORMAT_DER)
    pubkey = x509.get_pubkey().get_rsa()
    verify_EVP = EVP.PKey()
    verify_EVP.assign_rsa(pubkey)
    verify_EVP.reset_context(md='sha1')
    verify_EVP.verify_init()

    verify_EVP.verify_update(signed_info)
    result = verify_EVP.verify_final(signature.decode('base64'))

    return result
Ezra Nugroho
  • 224
  • 1
  • 7
  • Thanks! I'll switch back to this project when I get a chance and try it out. – Ennael Jul 05 '13 at 18:55
  • Sorry to comment on such an old question/answer. Does this verify the SAML token or assertion was un-touched or just verify the signature? I'm unclear if m2crypto is implementing HMAC under the hood and it's just the `verify_update()` value that needs to be expanded? – user3366016 Apr 02 '19 at 20:16
1

FYI, I was facing the same problem as you, and found no useful software for validating XML signatures in Python, so I wrote a new library: https://github.com/kislyuk/signxml.

from lxml import etree
from signxml import xmldsig

with open("saml2_idp_metadata.xml", "rb") as fh:
    cert = etree.parse(fh).find("//ds:X509Certificate").text

root = ElementTree.fromstring(signature_data)
xmldsig(root).verify(x509_cert=cert)
weaver
  • 1,763
  • 15
  • 16