Source code for saml.signature

# -*- coding: utf-8 -*-

"""
Sign and verify signatures using the `python-xmlsec` library.

.. autofunction:: sign
.. autofunction:: verify
"""


[docs]def sign(xml, stream, password=None): """ Sign an XML document with the given private key file. This will add a <Signature> element to the document. :param lxml.etree._Element xml: The document to sign :param file stream: The private key to sign the document with :param str password: The password used to access the private key :rtype: None Example usage: :: from saml import schema from lxml import etree document = schema.AuthenticationRequest() xml_document = document.serialize() with open('my_key_file.pem', 'r+') as stream: sign(xml_document, stream) print etree.tostring(xml_document) Produces the following XML document: .. code-block:: xml <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_6087de0b111b44349a70ff40191a4c0c" IssueInstant="2015-03-16T21:06:39Z"> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> <SignatureMethod Algorithm="http://www.w3.org/2000/ 09/xmldsig#rsa-sha1"/> <Reference> <Transforms> <Transform Algorithm="http://www.w3.org/2000/ 09/xmldsig#enveloped-signature"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/ 09/xmldsig#sha1"/> <DigestValue> 94O1FOjRE4JQYVDqStkYzne9StQ= </DigestValue> </Reference> </SignedInfo> <SignatureValue> aFYRRjtB3bDyLLJzLZmsn0K4SXmOpFYJ+8R8D31VojgiF37FOElbE56UFbm8BAjn l2AixrUGXP4djxoxxnfBD/reYw5yVuIVXlMxKec784nF2V4GyrfwJOKaNmlVPkq5 c8SI+EkKJ02mwiail0Zvjb9FzwvlYD+osMSXvJXVqnGHQDVFlhwbBRRVB6t44/M3 TzC4mLSVhuvcpsm4GTQSpGkHP7HvweKN/OTc0aTy8Kh/YUrImwnUCii+J0EW4nGg 71eZyq/IiSPnTD09WDHsWe3g29kpicZXqrQCWeLE2zfVKtyxxs7PyEmodH19jXyz wh9hQ8t6PFO47Ros5aV0bw== </SignatureValue> </Signature> </samlp:AuthnRequest> """ # Import xmlsec here to delay initializing the C library in # case we don't need it. import xmlsec # Resolve the SAML/2.0 element in question. from saml.schema.base import _element_registry element = _element_registry.get(xml.tag) # Create a signature template for RSA-SHA1 enveloped signature. signature_node = xmlsec.template.create( xml, xmlsec.Transform.EXCL_C14N, xmlsec.Transform.RSA_SHA1) # Add the <ds:Signature/> node to the document. xml.insert(element.meta.signature_index, signature_node) # Add the <ds:Reference/> node to the signature template. ref = xmlsec.template.add_reference( signature_node, xmlsec.Transform.SHA1) # Add the enveloped transform descriptor. xmlsec.template.add_transform(ref, xmlsec.Transform.ENVELOPED) # Create a digital signature context (no key manager is needed). ctx = xmlsec.SignatureContext() # Load private key. key = xmlsec.Key.from_memory(stream, xmlsec.KeyFormat.PEM, password) # Set the key on the context. ctx.key = key # Sign the template. ctx.sign(signature_node)
[docs]def verify(xml, stream): """ Verify the signaure of an XML document with the given certificate. Returns `True` if the document is signed with a valid signature. Returns `False` if the document is not signed or if the signature is invalid. :param lxml.etree._Element xml: The document to sign :param file stream: The private key to sign the document with :rtype: Boolean """ # Import xmlsec here to delay initializing the C library in # case we don't need it. import xmlsec # Find the <Signature/> node. signature_node = xmlsec.tree.find_node(xml, xmlsec.Node.SIGNATURE) if signature_node is None: # No `signature` node found; we cannot verify return False # Create a digital signature context (no key manager is needed). ctx = xmlsec.SignatureContext() # Register <Response/> and <Assertion/> ctx.register_id(xml) for assertion in xml.xpath("//*[local-name()='Assertion']"): ctx.register_id(assertion) # Load the public key. key = None for fmt in [ xmlsec.KeyFormat.PEM, xmlsec.KeyFormat.CERT_PEM]: stream.seek(0) try: key = xmlsec.Key.from_memory(stream, fmt) break except ValueError: # xmlsec now throws when it can't load the key pass # Set the key on the context. ctx.key = key # Verify the signature. try: ctx.verify(signature_node) return True except Exception: return False