I need to implement authentication in python from a 3rd party by using SAML2. I have looked into pysaml2 and found that to be quite confusing, and decided to give M2Crypto a chance after I found this question by Ennael.
The SAML token I receive can be found here. I have already extracted all the information I need from the Assertion
tag (the user's SSN, IP and the SAML tokens expiration window) but I can't get the verify_signature
function from Ennael (and the revised code from Ezra Nugroho) to return True. I have also tried to change verify_EVP.reset_context(md='sha1')
to verify_EVP.reset_context(md='sha256')
but that didn't work either.
I think my mistake must be in the signed_info part. What do I pass to verify_signature
for that part? Do I have to preprocess it in any way? I have been looking into the Transform tag but don't know where too look next.
Any help will be greatly appreciated. If someone needs the XML before the obfuscation to test and help me just PM me.
EDIT This is my code (very similar to the things i linked to. The main function is at the bottom):
def verify_signature(signed_info, cert, signature):
from M2Crypto import EVP, RSA, X509, m2
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)
return verify_EVP.verify_final(signature.decode('base64'))
def decode_response(resp):
return base64.b64decode(resp)
def get_xmldoc(xmlstring):
return XML(xmlstring)
def get_signature(doc):
return doc.find('{http://www.w3.org/2000/09/xmldsig#}Signature')
def get_signed_info(signature):
signed_info = signature.find(
'{http://www.w3.org/2000/09/xmldsig#}SignedInfo')
signed_info_str = tostring(signed_info)
# return parse(StringIO(signed_info_str))
return signed_info_str
def get_cert(signature):
ns = '{http://www.w3.org/2000/09/xmldsig#}'
keyinfo = signature.find('{}KeyInfo'.format(ns))
keydata = keyinfo.find('{}X509Data'.format(ns))
certelem = keydata.find('{}X509Certificate'.format(ns))
return certelem.text
def get_signature_value(signature):
return signature.find(
'{http://www.w3.org/2000/09/xmldsig#}SignatureValue').text
def parse_saml(saml):
dec_resp = decode_response(saml)
xml = get_xmldoc(dec_resp)
signature = get_signature(xml)
signed_info = get_signed_info(signature)
cert = get_cert(signature)
signature_value = get_signature_value(signature)
is_valid = verify_signature(signed_info, cert, signature_value)
UPDATE: Is it possible I need some more information from the 3rd party authentication provider? Do I need a private key for any of this?