예제 #1
0
def sign_envelope(envelope, key_file):
    """Sign the given soap request with the given key"""
    doc = etree.fromstring(envelope)
    body = get_body(doc)

    queue = SignQueue()
    queue.push_and_mark(body)

    security_node = ensure_security_header(doc, queue)
    security_token_node = create_binary_security_token(key_file)
    queue.push_and_mark(security_token_node)
    signature_node = Signature(xmlsec.TransformExclC14N,
                               xmlsec.TransformRsaSha1)

    security_node.append(security_token_node)
    security_node.append(signature_node)
    queue.insert_references(signature_node)

    key_info = create_key_info_node(security_token_node)
    signature_node.append(key_info)

    # Sign the generated xml
    xmlsec.addIDs(doc, ['Id'])
    dsigCtx = xmlsec.DSigCtx()
    dsigCtx.signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)
    dsigCtx.sign(signature_node)
    return etree.tostring(doc)
예제 #2
0
 def sign(self,doc, key):
     node = xmlsec.findNode(doc, xmlsec.dsig("Signature"))
     if node == None:
             print "Node was None"
     dsigCtx = xmlsec.DSigCtx()
     signKey = xmlsec.Key.load(key, xmlsec.KeyDataFormatPem, None)
     signKey.name = basename(key)
     dsigCtx.signKey = signKey
     dsigCtx.sign(node)
     return tostring(doc)
예제 #3
0
    def __build_signature(self,
                          saml_data,
                          relay_state,
                          saml_type,
                          sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1):
        """
        Builds the Signature
        :param saml_data: The SAML Data
        :type saml_data: string

        :param relay_state: The target URL the user should be redirected to
        :type relay_state: string

        :param saml_type: The target URL the user should be redirected to
        :type saml_type: string  SAMLRequest | SAMLResponse

        :param sign_algorithm: Signature algorithm method
        :type sign_algorithm: string
        """
        assert saml_type in ['SAMLRequest', 'SAMLResponse']

        # Load the key into the xmlsec context
        key = self.__settings.get_sp_key()

        if not key:
            raise OneLogin_Saml2_Error(
                "Trying to sign the %s but can't load the SP private key" %
                saml_type, OneLogin_Saml2_Error.SP_CERTS_NOT_FOUND)

        xmlsec.initialize()

        dsig_ctx = xmlsec.DSigCtx()
        dsig_ctx.signKey = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem,
                                                 None)

        saml_data_str = '%s=%s' % (saml_type, quote_plus(saml_data))
        relay_state_str = 'RelayState=%s' % quote_plus(relay_state)
        alg_str = 'SigAlg=%s' % quote_plus(sign_algorithm)

        sign_data = [saml_data_str, relay_state_str, alg_str]
        msg = '&'.join(sign_data)

        # Sign the metadata with our private key.
        sign_algorithm_transform_map = {
            OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.TransformDsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.TransformRsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.TransformRsaSha256,
            OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.TransformRsaSha384,
            OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.TransformRsaSha512
        }
        sign_algorithm_transform = sign_algorithm_transform_map.get(
            sign_algorithm, xmlsec.TransformRsaSha1)

        signature = dsig_ctx.signBinary(str(msg), sign_algorithm_transform)
        return b64encode(signature)
예제 #4
0
    def validate_binary_sign(signed_query,
                             signature,
                             cert=None,
                             algorithm=OneLogin_Saml2_Constants.RSA_SHA1,
                             debug=False):
        """
        Validates signed bynary data (Used to validate GET Signature).

        :param signed_query: The element we should validate
        :type: string


        :param signature: The signature that will be validate
        :type: string

        :param cert: The pubic cert
        :type: string

        :param algorithm: Signature algorithm
        :type: string

        :param debug: Activate the xmlsec debug
        :type: bool
        """
        try:
            xmlsec.initialize()

            if debug:
                xmlsec.set_error_callback(print_xmlsec_errors)

            dsig_ctx = xmlsec.DSigCtx()

            file_cert = OneLogin_Saml2_Utils.write_temp_file(cert)
            dsig_ctx.signKey = xmlsec.Key.load(file_cert.name,
                                               xmlsec.KeyDataFormatCertPem,
                                               None)
            file_cert.close()

            # Sign the metadata with our private key.
            sign_algorithm_transform_map = {
                OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.TransformDsaSha1,
                OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.TransformRsaSha1,
                OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.TransformRsaSha256,
                OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.TransformRsaSha384,
                OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.TransformRsaSha512
            }
            sign_algorithm_transform = sign_algorithm_transform_map.get(
                algorithm, xmlsec.TransformRsaSha1)

            dsig_ctx.verifyBinary(signed_query, sign_algorithm_transform,
                                  signature)
            return True
        except Exception:
            return False
예제 #5
0
    def testSignedHttpPostBinding(self):
        """
        Test to use the binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
        To sign a samlp:AuthnRequest you need to have a private key set for the service provider.
        To verify the assertion is signed correct you can also use the xmlsec1 command line tool:
        xmlsec1 --verify --id-attr:ID AuthnRequest --trusted-pem tests/certs/example.com/example.crt authn_signed_assertion.xml
        """
        filename = join(dirname(__file__), '..', '..', '..', 'settings',
                        'example_settings_http_post_binding.json')
        stream = open(filename, 'r')
        settings = json.load(stream)
        stream.close()

        settings = OneLogin_Saml2_Settings(settings)

        authn_request = OneLogin_Saml2_Authn_Request(settings)
        authn_request_encoded = authn_request.get_request()
        decoded = b64decode(authn_request_encoded)
        inflated = decompress(decoded, -15)

        sample_output_directory = join(dirname(__file__), '..', '..', '..',
                                       'sample_output')
        if not os.path.exists(sample_output_directory):
            os.makedirs(sample_output_directory)

        with open(
                join(dirname(__file__), '..', '..', '..',
                     'sample_output/authn_signed_assertion.xml'), 'wb') as f:
            f.write(inflated)

        # Turn the inflated xml (which is just a string) into a in memory XML document
        doc = fromstring(inflated)

        # Verification of enveloped signature
        node = doc.find(".//{%s}Signature" % xmlsec.DSigNs)
        key_file = join(dirname(__file__), '..', '..', '..',
                        'certs/example.com', 'example.pubkey')

        dsigCtx = xmlsec.DSigCtx()

        signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)
        signKey.name = 'example.pubkey'

        # Note: the assignment below effectively copies the key
        dsigCtx.signKey = signKey

        # Add ID attributes different from xml:id
        # See the Notes on https://pypi.python.org/pypi/dm.xmlsec.binding/1.3.2
        xmlsec.addIDs(doc, ["ID"])

        # This raises an exception if the document does not verify
        dsigCtx.verify(node)
예제 #6
0
파일: utils.py 프로젝트: seecr/python-saml
    def validate_binary_sign(signed_query,
                             signature,
                             cert=None,
                             algorithm=OneLogin_Saml2_Constants.RSA_SHA1,
                             debug=False):
        """
        Validates signed binary data (Used to validate GET Signature).

        :param signed_query: The element we should validate
        :type: string


        :param signature: The signature that will be validate
        :type: string

        :param cert: The public cert
        :type: string

        :param algorithm: Signature algorithm
        :type: string

        :param debug: Activate the xmlsec debug
        :type: bool

        :param raise_exceptions: Whether to return false on failure or raise an exception
        :type raise_exceptions: Boolean
        """
        error_callback_method = None
        if debug:
            error_callback_method = print_xmlsec_errors
        xmlsec.set_error_callback(error_callback_method)

        dsig_ctx = xmlsec.DSigCtx()
        dsig_ctx.signKey = xmlsec.Key.loadMemory(cert,
                                                 xmlsec.KeyDataFormatCertPem,
                                                 None)

        # Sign the metadata with our private key.
        sign_algorithm_transform_map = {
            OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.TransformDsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.TransformRsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.TransformRsaSha256,
            OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.TransformRsaSha384,
            OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.TransformRsaSha512
        }
        sign_algorithm_transform = sign_algorithm_transform_map.get(
            algorithm, xmlsec.TransformRsaSha1)

        dsig_ctx.verifyBinary(signed_query, sign_algorithm_transform,
                              signature)
        return True
예제 #7
0
def verify_envelope(reply, key_file):
    """Verify that the given soap request is signed with the certificate"""
    doc = etree.fromstring(reply)
    node = doc.find(".//{%s}Signature" % xmlsec.DSigNs)
    if node is None:
        raise CertificationError("No signature node found")
    dsigCtx = xmlsec.DSigCtx()

    xmlsec.addIDs(doc, ['Id'])
    signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem)
    signKey.name = os.path.basename(key_file)

    dsigCtx.signKey = signKey
    try:
        dsigCtx.verify(node)
    except xmlsec.VerificationError:
        return False
    return True
예제 #8
0
    def validate_binary_sign(signed_query,
                             signature,
                             cert=None,
                             algorithm=xmlsec.TransformRsaSha1,
                             debug=False):
        """
        Validates signed bynary data (Used to validate GET Signature).

        :param signed_query: The element we should validate
        :type: string


        :param signature: The signature that will be validate
        :type: string

        :param cert: The pubic cert
        :type: string

        :param algorithm: Signature algorithm
        :type: string

        :param debug: Activate the xmlsec debug
        :type: bool
        """
        try:
            xmlsec.initialize()

            if debug:
                xmlsec.set_error_callback(print_xmlsec_errors)

            dsig_ctx = xmlsec.DSigCtx()

            file_cert = OneLogin_Saml2_Utils.write_temp_file(cert)
            dsig_ctx.signKey = xmlsec.Key.load(file_cert.name,
                                               xmlsec.KeyDataFormatCertPem,
                                               None)
            file_cert.close()

            dsig_ctx.verifyBinary(signed_query, algorithm, signature)
            return True
        except Exception:
            return False
예제 #9
0
def sign_envelope(envelope, key_file, add_to_queue=None):
    """Sign the given soap request body with the given key. An optional add_to_queue callable can be
    passed to add additional elements to the signing queue. This function gets passed the document
    tree and should return a collection of Elements."""
    doc = etree.fromstring(envelope)
    body = get_body(doc)

    queue = SignQueue()
    queue.push_and_mark(body)

    if add_to_queue:
        if not hasattr(add_to_queue, '__call__'):
            raise ValueError('`zadd_to_queue` kwarg must be a callable')

        extra_sign_queue = add_to_queue(doc)
        if not hasattr(extra_sign_queue, '__iter__'):
            raise ValueError('`add_to_queue` must return an iterable value')

        for el in extra_sign_queue:
            queue.push_and_mark(el)

    security_node = ensure_security_header(doc, queue)
    security_token_node = create_binary_security_token(key_file)
    signature_node = Signature(xmlsec.TransformExclC14N,
                               xmlsec.TransformRsaSha1)

    security_node.append(security_token_node)
    security_node.append(signature_node)
    queue.insert_references(signature_node)

    key_info = create_key_info_node(security_token_node)
    signature_node.append(key_info)

    # Sign the generated xml
    xmlsec.addIDs(doc, ['Id'])
    dsigCtx = xmlsec.DSigCtx()
    dsigCtx.signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)
    dsigCtx.sign(signature_node)
    return etree.tostring(doc)
예제 #10
0
    def add_sign(xml,
                 key,
                 cert,
                 debug=False,
                 sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1,
                 digest_algorithm=OneLogin_Saml2_Constants.SHA1):
        """
        Adds signature key and senders certificate to an element (Message or
        Assertion).

        :param xml: The element we should sign
        :type: string | Document

        :param key: The private key
        :type: string

        :param cert: The public
        :type: string

        :param debug: Activate the xmlsec debug
        :type: bool

        :param sign_algorithm: Signature algorithm method
        :type sign_algorithm: string

        :param digest_algorithm: Digest algorithm method
        :type digest_algorithm: string

        :returns: Signed XML
        :rtype: string
        """
        if xml is None or xml == '':
            raise Exception('Empty string supplied as input')
        elif isinstance(xml, etree._Element):
            elem = xml
        elif isinstance(xml, Document):
            xml = xml.toxml()
            elem = fromstring(xml.encode('utf-8'))
        elif isinstance(xml, Element):
            xml.setAttributeNS(unicode(OneLogin_Saml2_Constants.NS_SAMLP),
                               'xmlns:samlp',
                               unicode(OneLogin_Saml2_Constants.NS_SAMLP))
            xml.setAttributeNS(unicode(OneLogin_Saml2_Constants.NS_SAML),
                               'xmlns:saml',
                               unicode(OneLogin_Saml2_Constants.NS_SAML))
            xml = xml.toxml()
            elem = fromstring(xml.encode('utf-8'))
        elif isinstance(xml, basestring):
            elem = fromstring(xml.encode('utf-8'))
        else:
            raise Exception('Error parsing xml string')

        error_callback_method = None
        if debug:
            error_callback_method = print_xmlsec_errors
        xmlsec.set_error_callback(error_callback_method)

        # Sign the metadata with our private key.
        sign_algorithm_transform_map = {
            OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.TransformDsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.TransformRsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.TransformRsaSha256,
            OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.TransformRsaSha384,
            OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.TransformRsaSha512
        }
        sign_algorithm_transform = sign_algorithm_transform_map.get(
            sign_algorithm, xmlsec.TransformRsaSha1)

        signature = Signature(xmlsec.TransformExclC14N,
                              sign_algorithm_transform)

        issuer = OneLogin_Saml2_Utils.query(elem, '//saml:Issuer')
        if len(issuer) > 0:
            issuer = issuer[0]
            issuer.addnext(signature)
        else:
            entity_descriptor = OneLogin_Saml2_Utils.query(
                elem, '//md:EntityDescriptor')
            if len(entity_descriptor) > 0:
                elem.insert(0, signature)
            else:
                elem[0].insert(0, signature)

        digest_algorithm_transform_map = {
            OneLogin_Saml2_Constants.SHA1: xmlsec.TransformSha1,
            OneLogin_Saml2_Constants.SHA256: xmlsec.TransformSha256,
            OneLogin_Saml2_Constants.SHA384: xmlsec.TransformSha384,
            OneLogin_Saml2_Constants.SHA512: xmlsec.TransformSha512
        }
        digest_algorithm_transform = digest_algorithm_transform_map.get(
            digest_algorithm, xmlsec.TransformSha1)

        ref = signature.addReference(digest_algorithm_transform)
        ref.addTransform(xmlsec.TransformEnveloped)
        ref.addTransform(xmlsec.TransformExclC14N)

        key_info = signature.ensureKeyInfo()
        key_info.addX509Data()

        dsig_ctx = xmlsec.DSigCtx()
        sign_key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None)

        file_cert = OneLogin_Saml2_Utils.write_temp_file(cert)
        sign_key.loadCert(file_cert.name, xmlsec.KeyDataFormatCertPem)
        file_cert.close()

        dsig_ctx.signKey = sign_key
        dsig_ctx.sign(signature)

        newdoc = parseString(
            tostring(elem, encoding='unicode').encode('utf-8'))

        signature_nodes = newdoc.getElementsByTagName("Signature")

        for signature in signature_nodes:
            signature.removeAttribute('xmlns')
            signature.setAttribute('xmlns:ds', OneLogin_Saml2_Constants.NS_DS)
            if not signature.tagName.startswith('ds:'):
                signature.tagName = 'ds:' + signature.tagName
            nodes = signature.getElementsByTagName("*")
            for node in nodes:
                if not node.tagName.startswith('ds:'):
                    node.tagName = 'ds:' + node.tagName

        return newdoc.saveXML(newdoc.firstChild)
예제 #11
0
    def validate_node_sign(signature_node,
                           elem,
                           cert=None,
                           fingerprint=None,
                           fingerprintalg='sha1',
                           validatecert=False,
                           debug=False):
        """
        Validates a signature node.

        :param signature_node: The signature node
        :type: Node

        :param xml: The element we should validate
        :type: Document

        :param cert: The public cert
        :type: string

        :param fingerprint: The fingerprint of the public cert
        :type: string

        :param fingerprintalg: The algorithm used to build the fingerprint
        :type: string

        :param validatecert: If true, will verify the signature and if the cert is valid.
        :type: bool

        :param debug: Activate the xmlsec debug
        :type: bool

        :param raise_exceptions: Whether to return false on failure or raise an exception
        :type raise_exceptions: Boolean
        """
        error_callback_method = None
        if debug:
            error_callback_method = print_xmlsec_errors
        xmlsec.set_error_callback(error_callback_method)

        xmlsec.addIDs(elem, ["ID"])

        if (cert is None or cert == '') and fingerprint:
            x509_certificate_nodes = OneLogin_Saml2_Utils.query(
                signature_node,
                '//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate')
            if len(x509_certificate_nodes) > 0:
                x509_certificate_node = x509_certificate_nodes[0]
                x509_cert_value = x509_certificate_node.text
                x509_fingerprint_value = OneLogin_Saml2_Utils.calculate_x509_fingerprint(
                    x509_cert_value, fingerprintalg)
                if fingerprint == x509_fingerprint_value:
                    cert = OneLogin_Saml2_Utils.format_cert(x509_cert_value)

        # Check if Reference URI is empty
        # reference_elem = OneLogin_Saml2_Utils.query(signature_node, '//ds:Reference')
        # if len(reference_elem) > 0:
        #    if reference_elem[0].get('URI') == '':
        #        reference_elem[0].set('URI', '#%s' % signature_node.getparent().get('ID'))

        if cert is None or cert == '':
            raise OneLogin_Saml2_Error(
                'Could not validate node signature: No certificate provided.',
                OneLogin_Saml2_Error.CERT_NOT_FOUND)

        file_cert = OneLogin_Saml2_Utils.write_temp_file(cert)

        if validatecert:
            mngr = xmlsec.KeysMngr()
            mngr.loadCert(file_cert.name, xmlsec.KeyDataFormatCertPem,
                          xmlsec.KeyDataTypeTrusted)
            dsig_ctx = xmlsec.DSigCtx(mngr)
        else:
            dsig_ctx = xmlsec.DSigCtx()
            dsig_ctx.signKey = xmlsec.Key.load(file_cert.name,
                                               xmlsec.KeyDataFormatCertPem,
                                               None)

        file_cert.close()

        dsig_ctx.setEnabledKeyData([xmlsec.KeyDataX509])

        try:
            dsig_ctx.verify(signature_node)
        except Exception as err:
            raise OneLogin_Saml2_ValidationError(
                'Signature validation failed. SAML Response rejected. %s',
                OneLogin_Saml2_ValidationError.INVALID_SIGNATURE,
                err.__str__())

        return True
예제 #12
0
파일: utils.py 프로젝트: skarra/python-saml
    def validate_node_sign(signature_node,
                           elem,
                           cert=None,
                           fingerprint=None,
                           fingerprintalg='sha1',
                           validatecert=False,
                           debug=False):
        """
        Validates a signature node.

        :param signature_node: The signature node
        :type: Node

        :param xml: The element we should validate
        :type: Document

        :param cert: The public cert
        :type: string

        :param fingerprint: The fingerprint of the public cert
        :type: string

        :param fingerprintalg: The algorithm used to build the fingerprint
        :type: string

        :param validatecert: If true, will verify the signature and if the cert is valid.
        :type: bool

        :param debug: Activate the xmlsec debug
        :type: bool
        """
        try:
            if debug:
                xmlsec.set_error_callback(print_xmlsec_errors)

            xmlsec.addIDs(elem, ["ID"])

            if (cert is None or cert == '') and fingerprint:
                x509_certificate_nodes = OneLogin_Saml2_Utils.query(
                    signature_node,
                    '//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate')
                if len(x509_certificate_nodes) > 0:
                    x509_certificate_node = x509_certificate_nodes[0]
                    x509_cert_value = x509_certificate_node.text
                    x509_fingerprint_value = OneLogin_Saml2_Utils.calculate_x509_fingerprint(
                        x509_cert_value, fingerprintalg)
                    if fingerprint == x509_fingerprint_value:
                        cert = OneLogin_Saml2_Utils.format_cert(
                            x509_cert_value)

            # Check if Reference URI is empty
            # reference_elem = OneLogin_Saml2_Utils.query(signature_node, '//ds:Reference')
            # if len(reference_elem) > 0:
            #    if reference_elem[0].get('URI') == '':
            #        reference_elem[0].set('URI', '#%s' % signature_node.getparent().get('ID'))

            if cert is None or cert == '':
                return False

            file_cert = OneLogin_Saml2_Utils.write_temp_file(cert)

            if validatecert:
                mngr = xmlsec.KeysMngr()
                mngr.loadCert(file_cert.name, xmlsec.KeyDataFormatCertPem,
                              xmlsec.KeyDataTypeTrusted)
                dsig_ctx = xmlsec.DSigCtx(mngr)
            else:
                dsig_ctx = xmlsec.DSigCtx()
                dsig_ctx.signKey = xmlsec.Key.load(file_cert.name,
                                                   xmlsec.KeyDataFormatCertPem,
                                                   None)

            file_cert.close()

            dsig_ctx.setEnabledKeyData([xmlsec.KeyDataX509])
            dsig_ctx.verify(signature_node)
            return True
        except Exception:
            return False
예제 #13
0
    def add_sign_with_id(xml,
                         uid,
                         key,
                         cert,
                         debug=False,
                         sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1,
                         digest_algorithm=OneLogin_Saml2_Constants.SHA1):

        # thanks to https://github.com/onelogin/python-saml/pull/78/files for the help. credit to @tachang

        xmlsec.initialize()
        xmlsec.set_error_callback(print_xmlsec_errors)
        #
        sign_algorithm_transform_map = {
            OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.TransformDsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.TransformRsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.TransformRsaSha256,
            OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.TransformRsaSha384,
            OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.TransformRsaSha512
        }
        sign_algorithm_transform = sign_algorithm_transform_map.get(
            sign_algorithm, xmlsec.TransformRsaSha1)

        signature = Signature(xmlsec.TransformExclC14N,
                              sign_algorithm_transform)

        if xml is None or xml == '':
            raise Exception('Empty string supplied as input')
        elif isinstance(xml, etree._Element):
            doc = xml
        elif isinstance(xml, Document):
            xml = xml.toxml()
            doc = fromstring(str(xml))
        elif isinstance(xml, Element):
            xml.setAttributeNS(unicode(OneLogin_Saml2_Constants.NS_SAMLP),
                               'xmlns:samlp',
                               unicode(OneLogin_Saml2_Constants.NS_SAMLP))
            xml.setAttributeNS(unicode(OneLogin_Saml2_Constants.NS_SAML),
                               'xmlns:saml',
                               unicode(OneLogin_Saml2_Constants.NS_SAML))
            xml = xml.toxml()
            doc = fromstring(str(xml))
        elif isinstance(xml, basestring):
            doc = fromstring(str(xml))
        else:
            raise Exception('Error parsing xml string')

        # # ID attributes different from xml:id must be made known by the application through a call
        # # to the addIds(node, ids) function defined by xmlsec.
        xmlsec.addIDs(doc, ['ID'])

        doc.insert(0, signature)

        digest_algorithm_transform_map = {
            OneLogin_Saml2_Constants.SHA1: xmlsec.TransformSha1,
            OneLogin_Saml2_Constants.SHA256: xmlsec.TransformSha256
        }

        digest_algorithm_transform = digest_algorithm_transform_map.get(
            digest_algorithm, xmlsec.TransformRsaSha1)

        ref = signature.addReference(digest_algorithm_transform,
                                     uri="#%s" % uid)
        ref.addTransform(xmlsec.TransformEnveloped)
        ref.addTransform(xmlsec.TransformExclC14N)

        key_info = signature.ensureKeyInfo()
        key_info.addKeyName()
        key_info.addX509Data()

        dsig_ctx = xmlsec.DSigCtx()

        sign_key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None)

        from tempfile import NamedTemporaryFile
        cert_file = NamedTemporaryFile(delete=True)
        cert_file.write(cert)
        cert_file.seek(0)

        sign_key.loadCert(cert_file.name, xmlsec.KeyDataFormatPem)

        dsig_ctx.signKey = sign_key

        # # Note: the assignment below effectively copies the key
        dsig_ctx.sign(signature)

        newdoc = parseString(etree.tostring(doc))

        return newdoc.saveXML(newdoc.firstChild)
예제 #14
0
    def validate_sign(xml, cert=None, fingerprint=None, validatecert=False, debug=False):
        """
        Validates a signature (Message or Assertion).

        :param xml: The element we should validate
        :type: string | Document

        :param cert: The pubic cert
        :type: string

        :param fingerprint: The fingerprint of the public cert
        :type: string

        :param validatecert: If true, will verify the signature and if the cert is valid.
        :type: bool

        :param debug: Activate the xmlsec debug
        :type: bool
        """
        try:
            if xml is None or xml == '':
                raise Exception('Empty string supplied as input')
            elif isinstance(xml, etree._Element):
                elem = xml
            elif isinstance(xml, Document):
                xml = xml.toxml()
                elem = fromstring(str(xml))
            elif isinstance(xml, Element):
                xml.setAttributeNS(
                    unicode(OneLogin_Saml2_Constants.NS_SAMLP),
                    'xmlns:samlp',
                    unicode(OneLogin_Saml2_Constants.NS_SAMLP)
                )
                xml.setAttributeNS(
                    unicode(OneLogin_Saml2_Constants.NS_SAML),
                    'xmlns:saml',
                    unicode(OneLogin_Saml2_Constants.NS_SAML)
                )
                xml = xml.toxml()
                elem = fromstring(str(xml))
            elif isinstance(xml, basestring):
                elem = fromstring(str(xml))
            else:
                raise Exception('Error parsing xml string')

            xmlsec.initialize()

            if debug:
                xmlsec.set_error_callback(print_xmlsec_errors)

            xmlsec.addIDs(elem, ["ID"])

            signature_nodes = OneLogin_Saml2_Utils.query(elem, '//ds:Signature')

            if len(signature_nodes) > 0:
                signature_node = signature_nodes[0]

                if (cert is None or cert == '') and fingerprint:
                    x509_certificate_nodes = OneLogin_Saml2_Utils.query(signature_node, '//ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate')
                    if len(x509_certificate_nodes) > 0:
                        x509_certificate_node = x509_certificate_nodes[0]
                        x509_cert_value = x509_certificate_node.text
                        x509_fingerprint_value = OneLogin_Saml2_Utils.calculate_x509_fingerprint(x509_cert_value)
                        if fingerprint == x509_fingerprint_value:
                            cert = OneLogin_Saml2_Utils.format_cert(x509_cert_value)

                if cert is None or cert == '':
                    return False

                dsig_ctx = xmlsec.DSigCtx()

                file_cert = OneLogin_Saml2_Utils.write_temp_file(cert)

                if validatecert:
                    mngr = xmlsec.KeysMngr()
                    mngr.loadCert(file_cert.name, xmlsec.KeyDataFormatCertPem, xmlsec.KeyDataTypeTrusted)
                    dsig_ctx = xmlsec.DSigCtx(mngr)
                else:
                    dsig_ctx = xmlsec.DSigCtx()
                    dsig_ctx.signKey = xmlsec.Key.load(file_cert.name, xmlsec.KeyDataFormatCertPem, None)

                file_cert.close()

                dsig_ctx.setEnabledKeyData([xmlsec.KeyDataX509])
                dsig_ctx.verify(signature_node)
                return True
            else:
                return False
        except Exception:
            return False
예제 #15
0
    def add_sign(xml, key, cert, debug=False):
        """
        Adds signature key and senders certificate to an element (Message or
        Assertion).

        :param xml: The element we should sign
        :type: string | Document

        :param key: The private key
        :type: string

        :param debug: Activate the xmlsec debug
        :type: bool

        :param cert: The public
        :type: string
        """
        if xml is None or xml == '':
            raise Exception('Empty string supplied as input')
        elif isinstance(xml, etree._Element):
            elem = xml
        elif isinstance(xml, Document):
            xml = xml.toxml()
            elem = fromstring(str(xml))
        elif isinstance(xml, Element):
            xml.setAttributeNS(
                unicode(OneLogin_Saml2_Constants.NS_SAMLP),
                'xmlns:samlp',
                unicode(OneLogin_Saml2_Constants.NS_SAMLP)
            )
            xml.setAttributeNS(
                unicode(OneLogin_Saml2_Constants.NS_SAML),
                'xmlns:saml',
                unicode(OneLogin_Saml2_Constants.NS_SAML)
            )
            xml = xml.toxml()
            elem = fromstring(str(xml))
        elif isinstance(xml, basestring):
            elem = fromstring(str(xml))
        else:
            raise Exception('Error parsing xml string')

        xmlsec.initialize()

        if debug:
            xmlsec.set_error_callback(print_xmlsec_errors)

        # Sign the metadacta with our private key.
        signature = Signature(xmlsec.TransformExclC14N, xmlsec.TransformRsaSha1)

        issuer = OneLogin_Saml2_Utils.query(elem, '//saml:Issuer')
        if len(issuer) > 0:
            issuer = issuer[0]
            issuer.addnext(signature)
        else:
            elem[0].insert(0, signature)

        ref = signature.addReference(xmlsec.TransformSha1)
        ref.addTransform(xmlsec.TransformEnveloped)
        ref.addTransform(xmlsec.TransformExclC14N)

        key_info = signature.ensureKeyInfo()
        key_info.addX509Data()

        dsig_ctx = xmlsec.DSigCtx()
        sign_key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None)

        file_cert = OneLogin_Saml2_Utils.write_temp_file(cert)
        sign_key.loadCert(file_cert.name, xmlsec.KeyDataFormatCertPem)
        file_cert.close()

        dsig_ctx.signKey = sign_key
        dsig_ctx.sign(signature)

        newdoc = parseString(etree.tostring(elem))

        signature_nodes = newdoc.getElementsByTagName("Signature")

        for signature in signature_nodes:
            signature.removeAttribute('xmlns')
            signature.setAttribute('xmlns:ds', OneLogin_Saml2_Constants.NS_DS)
            if not signature.tagName.startswith('ds:'):
                signature.tagName = 'ds:' + signature.tagName
            nodes = signature.getElementsByTagName("*")
            for node in nodes:
                if not node.tagName.startswith('ds:'):
                    node.tagName = 'ds:' + node.tagName

        return newdoc.saveXML(newdoc.firstChild)
예제 #16
0
    def add_sign(xml,
                 key,
                 cert,
                 debug=False,
                 sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1,
                 digest_algorithm=OneLogin_Saml2_Constants.SHA1):
        """
        Adds signature key and senders certificate to an element (Message or
        Assertion).

        :param xml: The element we should sign
        :type: string | Document

        :param key: The private key
        :type: string

        :param cert: The public
        :type: string

        :param debug: Activate the xmlsec debug
        :type: bool

        :param sign_algorithm: Signature algorithm method
        :type sign_algorithm: string

        :param digest_algorithm: Digest algorithm method
        :type digest_algorithm: string

        :returns: Signed XML
        :rtype: string
        """
        if xml is None or xml == '':
            raise Exception('Empty string supplied as input')
        elif isinstance(xml, etree._Element):
            elem = xml
        elif isinstance(xml, Document):
            xml = xml.toxml()
            elem = fromstring(xml.encode('utf-8'), forbid_dtd=True)
        elif isinstance(xml, Element):
            xml.setAttributeNS(unicode(OneLogin_Saml2_Constants.NS_SAMLP),
                               'xmlns:samlp',
                               unicode(OneLogin_Saml2_Constants.NS_SAMLP))
            xml.setAttributeNS(unicode(OneLogin_Saml2_Constants.NS_SAML),
                               'xmlns:saml',
                               unicode(OneLogin_Saml2_Constants.NS_SAML))
            xml = xml.toxml()
            elem = fromstring(xml.encode('utf-8'), forbid_dtd=True)
        elif isinstance(xml, basestring):
            elem = fromstring(xml.encode('utf-8'), forbid_dtd=True)
        else:
            raise Exception('Error parsing xml string')

        error_callback_method = None
        if debug:
            error_callback_method = print_xmlsec_errors
        xmlsec.set_error_callback(error_callback_method)

        sign_algorithm_transform_map = {
            OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.TransformDsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.TransformRsaSha1,
            OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.TransformRsaSha256,
            OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.TransformRsaSha384,
            OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.TransformRsaSha512
        }
        sign_algorithm_transform = sign_algorithm_transform_map.get(
            sign_algorithm, xmlsec.TransformRsaSha1)

        signature = Signature(xmlsec.TransformExclC14N,
                              sign_algorithm_transform,
                              nsPrefix='ds')

        issuer = OneLogin_Saml2_Utils.query(elem, '//saml:Issuer')
        if len(issuer) > 0:
            issuer = issuer[0]
            issuer.addnext(signature)
            elem_to_sign = issuer.getparent()
        else:
            entity_descriptor = OneLogin_Saml2_Utils.query(
                elem, '//md:EntityDescriptor')
            if len(entity_descriptor) > 0:
                elem.insert(0, signature)
            else:
                elem[0].insert(0, signature)
            elem_to_sign = elem

        elem_id = elem_to_sign.get('ID', None)
        if elem_id is not None:
            if elem_id:
                elem_id = '#' + elem_id
        else:
            generated_id = generated_id = OneLogin_Saml2_Utils.generate_unique_id(
            )
            elem_id = '#' + generated_id
            elem_to_sign.attrib['ID'] = generated_id

        xmlsec.addIDs(elem_to_sign, ["ID"])

        digest_algorithm_transform_map = {
            OneLogin_Saml2_Constants.SHA1: xmlsec.TransformSha1,
            OneLogin_Saml2_Constants.SHA256: xmlsec.TransformSha256,
            OneLogin_Saml2_Constants.SHA384: xmlsec.TransformSha384,
            OneLogin_Saml2_Constants.SHA512: xmlsec.TransformSha512
        }
        digest_algorithm_transform = digest_algorithm_transform_map.get(
            digest_algorithm, xmlsec.TransformSha1)

        ref = signature.addReference(digest_algorithm_transform)
        if elem_id:
            ref.attrib['URI'] = elem_id

        ref.addTransform(xmlsec.TransformEnveloped)
        ref.addTransform(xmlsec.TransformExclC14N)

        key_info = signature.ensureKeyInfo()
        key_info.addX509Data()

        dsig_ctx = xmlsec.DSigCtx()
        sign_key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None)

        file_cert = OneLogin_Saml2_Utils.write_temp_file(cert)
        sign_key.loadCert(file_cert.name, xmlsec.KeyDataFormatCertPem)
        file_cert.close()

        dsig_ctx.signKey = sign_key
        dsig_ctx.sign(signature)

        return tostring(elem, encoding='unicode').encode('utf-8')