在python中验证SAML签名

时间:2014-01-18 20:33:15

标签: python xml authentication saml m2crypto

我需要使用SAML2从第三方在python中实现身份验证。我调查了pysaml2并发现相当混乱,并且在M2Crypto找到this question之后决定给Ennael一个机会。

我收到的SAML令牌can be found here。我已经从Assertion标记(用户的SSN,IP和SAML令牌到期窗口)中提取了我需要的所有信息,但是我无法从Ennael获取verify_signature函数(以及{{ 3}}从revised code)返回True。我还尝试将verify_EVP.reset_context(md='sha1')更改为verify_EVP.reset_context(md='sha256'),但这也无效。

我认为我的错误必须在signed_info部分。对于那部分我应该传递给verify_signature什么?我是否必须以任何方式预处理它?我一直在研究变换标签,但不知道接下来会在哪里看。

任何帮助将不胜感激。如果有人在混淆之前需要XML来测试并帮助我PM我。

编辑这是我的代码(与我链接的内容非常相似。主要功能在底部):

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)

更新:我是否可能需要来自第三方身份验证提供商的更多信息?我是否需要私钥?

2 个答案:

答案 0 :(得分:2)

我遇到了同样的问题,不得不为它开发一个模块:https://github.com/kislyuk/signxml。我选择仅依赖于PyCrypto和pyOpenSSL,因为M2Crypto不太受欢迎且维护得不好,这从兼容性(例如PyPy)和安全性角度来看都是一个危险。我还使用lxml进行规范化(c14n)。来自signxml docs:

from signxml import xmldsig

cert = open("example.pem").read()
key = open("example.key").read()
root = ElementTree.fromstring(data)
xmldsig(root).verify()

答案 1 :(得分:1)

在验证签名之前,您需要规范化签名信息。这就是转换标签所暗示的内容。基本上,由于相同的XML格式可以不同,因此需要以规范格式验证XML签名。