예제 #1
0
    def __init__(self, settings, response):
        """
        Constructs the response object.

        :param settings: The setting info
        :type settings: OneLogin_Saml2_Setting object

        :param response: The base64 encoded, XML string containing the samlp:Response
        :type response: string
        """
        self.__settings = settings
        self.__error = None
        self.response = OneLogin_Saml2_Utils.b64decode(response)
        self.document = OneLogin_Saml2_XML.to_etree(self.response)
        self.decrypted_document = None
        self.encrypted = None
        self.valid_scd_not_on_or_after = None

        # Quick check for the presence of EncryptedAssertion
        encrypted_assertion_nodes = self.__query(
            '/samlp:Response/saml:EncryptedAssertion')
        if encrypted_assertion_nodes:
            decrypted_document = deepcopy(self.document)
            self.encrypted = True
            self.decrypted_document = self.__decrypt_assertion(
                decrypted_document)
예제 #2
0
    def get_id(request):
        """
        Returns the ID of the Logout Request
        :param request: Logout Request Message
        :type request: string|DOMDocument
        :return: string ID
        :rtype: str object
        """

        elem = OneLogin_Saml2_XML.to_etree(request)
        return elem.get('ID', None)
예제 #3
0
    def get_issuer(request):
        """
        Gets the Issuer of the Logout Request Message
        :param request: Logout Request Message
        :type request: string|DOMDocument
        :return: The Issuer
        :rtype: string
        """

        elem = OneLogin_Saml2_XML.to_etree(request)
        issuer = None
        issuer_nodes = OneLogin_Saml2_XML.query(
            elem, '/samlp:LogoutRequest/saml:Issuer')
        if len(issuer_nodes) == 1:
            issuer = OneLogin_Saml2_XML.element_text(issuer_nodes[0])
        return issuer
예제 #4
0
    def get_session_indexes(request):
        """
        Gets the SessionIndexes from the Logout Request
        :param request: Logout Request Message
        :type request: string|DOMDocument
        :return: The SessionIndex value
        :rtype: list
        """

        elem = OneLogin_Saml2_XML.to_etree(request)
        session_indexes = []
        session_index_nodes = OneLogin_Saml2_XML.query(
            elem, '/samlp:LogoutRequest/samlp:SessionIndex')
        for session_index_node in session_index_nodes:
            session_indexes.append(
                OneLogin_Saml2_XML.element_text(session_index_node))
        return session_indexes
예제 #5
0
    def get_nameid_data(request, key=None):
        """
        Gets the NameID Data of the the Logout Request
        :param request: Logout Request Message
        :type request: string|DOMDocument
        :param key: The SP key
        :type key: string
        :return: Name ID Data (Value, Format, NameQualifier, SPNameQualifier)
        :rtype: dict
        """
        elem = OneLogin_Saml2_XML.to_etree(request)
        name_id = None
        encrypted_entries = OneLogin_Saml2_XML.query(
            elem, '/samlp:LogoutRequest/saml:EncryptedID')

        if len(encrypted_entries) == 1:
            if key is None:
                raise OneLogin_Saml2_Error(
                    'Private Key is required in order to decrypt the NameID, check settings',
                    OneLogin_Saml2_Error.PRIVATE_KEY_NOT_FOUND)

            encrypted_data_nodes = OneLogin_Saml2_XML.query(
                elem,
                '/samlp:LogoutRequest/saml:EncryptedID/xenc:EncryptedData')
            if len(encrypted_data_nodes) == 1:
                encrypted_data = encrypted_data_nodes[0]
                name_id = OneLogin_Saml2_Utils.decrypt_element(
                    encrypted_data, key)
        else:
            entries = OneLogin_Saml2_XML.query(
                elem, '/samlp:LogoutRequest/saml:NameID')
            if len(entries) == 1:
                name_id = entries[0]

        if name_id is None:
            raise OneLogin_Saml2_ValidationError(
                'NameID not found in the Logout Request',
                OneLogin_Saml2_ValidationError.NO_NAMEID)

        name_id_data = {'Value': OneLogin_Saml2_XML.element_text(name_id)}
        for attr in ['Format', 'SPNameQualifier', 'NameQualifier']:
            if attr in name_id.attrib:
                name_id_data[attr] = name_id.attrib[attr]

        return name_id_data
예제 #6
0
    def __init__(self, settings, response=None):
        """
        Constructs a Logout Response object (Initialize params from settings
        and if provided load the Logout Response.

        Arguments are:
            * (OneLogin_Saml2_Settings)   settings. Setting data
            * (string)                    response. An UUEncoded SAML Logout
                                                    response from the IdP.
        """
        self.__settings = settings
        self.__error = None
        self.id = None

        if response is not None:
            self.__logout_response = compat.to_string(
                OneLogin_Saml2_Utils.decode_base64_and_inflate(
                    response, ignore_zip=True))
            self.document = OneLogin_Saml2_XML.to_etree(self.__logout_response)
            self.id = self.document.get('ID', None)
예제 #7
0
    def add_x509_key_descriptors(metadata, cert=None, add_encryption=True):
        """
        Adds the x509 descriptors (sign/encryption) to the metadata
        The same cert will be used for sign/encrypt

        :param metadata: SAML Metadata XML
        :type metadata: string

        :param cert: x509 cert
        :type cert: string

        :param add_encryption: Determines if the KeyDescriptor[use="encryption"] should be added.
        :type add_encryption: boolean

        :returns: Metadata with KeyDescriptors
        :rtype: string
        """
        if cert is None or cert == '':
            return metadata
        try:
            root = OneLogin_Saml2_XML.to_etree(metadata)
        except Exception as e:
            raise Exception('Error parsing metadata. ' + str(e))

        assert root.tag == '{%s}EntityDescriptor' % OneLogin_Saml2_Constants.NS_MD
        try:
            sp_sso_descriptor = next(
                root.iterfind('.//md:SPSSODescriptor',
                              namespaces=OneLogin_Saml2_Constants.NSMAP))
        except StopIteration:
            raise Exception('Malformed metadata.')

        if add_encryption:
            OneLogin_Saml2_Metadata.__add_x509_key_descriptors(
                sp_sso_descriptor, cert, False)
        OneLogin_Saml2_Metadata.__add_x509_key_descriptors(
            sp_sso_descriptor, cert, True)
        return OneLogin_Saml2_XML.to_string(root)
예제 #8
0
    def get_metadata(url, validate_cert=True):
        """
        Gets the metadata XML from the provided URL
        :param url: Url where the XML of the Identity Provider Metadata is published.
        :type url: string

        :param validate_cert: If the url uses https schema, that flag enables or not the verification of the associated certificate.
        :type validate_cert: bool

        :returns: metadata XML
        :rtype: string
        """
        valid = False

        if validate_cert:
            response = urllib2.urlopen(url)
        else:
            ctx = ssl.create_default_context()
            ctx.check_hostname = False
            ctx.verify_mode = ssl.CERT_NONE
            response = urllib2.urlopen(url, context=ctx)
        xml = response.read()

        if xml:
            try:
                dom = OneLogin_Saml2_XML.to_etree(xml)
                idp_descriptor_nodes = OneLogin_Saml2_XML.query(
                    dom, '//md:IDPSSODescriptor')
                if idp_descriptor_nodes:
                    valid = True
            except Exception:
                pass

        if not valid:
            raise Exception('Not valid IdP XML found from URL: %s' % (url))

        return xml
예제 #9
0
파일: utils.py 프로젝트: corey-ryan/opensso
    def decrypt_element(encrypted_data, key, debug=False, inplace=False):
        """
        Decrypts an encrypted element.

        :param encrypted_data: The encrypted data.
        :type: lxml.etree.Element | DOMElement | basestring

        :param key: The key.
        :type: string

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

        :param inplace: update passed data with decrypted result
        :type: bool

        :returns: The decrypted element.
        :rtype: lxml.etree.Element
        """

        if isinstance(encrypted_data, Element):
            encrypted_data = OneLogin_Saml2_XML.to_etree(
                str(encrypted_data.toxml()))
        if not inplace and isinstance(encrypted_data,
                                      OneLogin_Saml2_XML._element_class):
            encrypted_data = deepcopy(encrypted_data)
        elif isinstance(encrypted_data, OneLogin_Saml2_XML._text_class):
            encrypted_data = OneLogin_Saml2_XML._parse_etree(encrypted_data)

        xmlsec.enable_debug_trace(debug)
        manager = xmlsec.KeysManager()

        manager.add_key(xmlsec.Key.from_memory(key, xmlsec.KeyFormat.PEM,
                                               None))
        enc_ctx = xmlsec.EncryptionContext(manager)
        return enc_ctx.decrypt(encrypted_data)
예제 #10
0
    def is_valid(self, request_data, raise_exceptions=False):
        """
        Checks if the Logout Request received is valid
        :param request_data: Request Data
        :type request_data: dict

        :param raise_exceptions: Whether to return false on failure or raise an exception
        :type raise_exceptions: Boolean

        :return: If the Logout Request is or not valid
        :rtype: boolean
        """
        self.__error = None
        try:
            root = OneLogin_Saml2_XML.to_etree(self.__logout_request)

            idp_data = self.__settings.get_idp_data()
            idp_entity_id = idp_data['entityId']

            get_data = ('get_data' in request_data
                        and request_data['get_data']) or dict()

            if self.__settings.is_strict():
                res = OneLogin_Saml2_XML.validate_xml(
                    root, 'saml-schema-protocol-2.0.xsd',
                    self.__settings.is_debug_active())
                if isinstance(res, str):
                    raise OneLogin_Saml2_ValidationError(
                        'Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd',
                        OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT)

                security = self.__settings.get_security_data()

                current_url = OneLogin_Saml2_Utils.get_self_url_no_query(
                    request_data)

                # Check NotOnOrAfter
                if root.get('NotOnOrAfter', None):
                    na = OneLogin_Saml2_Utils.parse_SAML_to_time(
                        root.get('NotOnOrAfter'))
                    if na <= OneLogin_Saml2_Utils.now():
                        raise OneLogin_Saml2_ValidationError(
                            'Could not validate timestamp: expired. Check system clock.)',
                            OneLogin_Saml2_ValidationError.RESPONSE_EXPIRED)

                # Check destination
                if root.get('Destination', None):
                    destination = root.get('Destination')
                    if destination != '':
                        if current_url not in destination:
                            raise OneLogin_Saml2_ValidationError(
                                'The LogoutRequest was received at '
                                '%(currentURL)s instead of %(destination)s' % {
                                    'currentURL': current_url,
                                    'destination': destination,
                                }, OneLogin_Saml2_ValidationError.
                                WRONG_DESTINATION)

                # Check issuer
                issuer = OneLogin_Saml2_Logout_Request.get_issuer(root)
                if issuer is not None and issuer != idp_entity_id:
                    raise OneLogin_Saml2_ValidationError(
                        'Invalid issuer in the Logout Request (expected %(idpEntityId)s, got %(issuer)s)'
                        % {
                            'idpEntityId': idp_entity_id,
                            'issuer': issuer
                        }, OneLogin_Saml2_ValidationError.WRONG_ISSUER)

                if security['wantMessagesSigned']:
                    if 'Signature' not in get_data:
                        raise OneLogin_Saml2_ValidationError(
                            'The Message of the Logout Request is not signed and the SP require it',
                            OneLogin_Saml2_ValidationError.NO_SIGNED_MESSAGE)

            return True
        except Exception as err:
            # pylint: disable=R0801
            self.__error = str(err)
            debug = self.__settings.is_debug_active()
            if debug:
                print(err)
            if raise_exceptions:
                raise
            return False
예제 #11
0
    def parse(idp_metadata,
              required_sso_binding=OneLogin_Saml2_Constants.
              BINDING_HTTP_REDIRECT,
              required_slo_binding=OneLogin_Saml2_Constants.
              BINDING_HTTP_REDIRECT,
              entity_id=None):
        """
        Parses the Identity Provider metadata and return a dict with extracted data.

        If there are multiple <IDPSSODescriptor> tags, parse only the first.

        Parses only those SSO endpoints with the same binding as given by
        the `required_sso_binding` parameter.

        Parses only those SLO endpoints with the same binding as given by
        the `required_slo_binding` parameter.

        If the metadata specifies multiple SSO endpoints with the required
        binding, extract only the first (the same holds true for SLO
        endpoints).

        :param idp_metadata: XML of the Identity Provider Metadata.
        :type idp_metadata: string

        :param required_sso_binding: Parse only POST or REDIRECT SSO endpoints.
        :type required_sso_binding: one of OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT
            or OneLogin_Saml2_Constants.BINDING_HTTP_POST

        :param required_slo_binding: Parse only POST or REDIRECT SLO endpoints.
        :type required_slo_binding: one of OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT
            or OneLogin_Saml2_Constants.BINDING_HTTP_POST

        :param entity_id: Specify the entity_id of the EntityDescriptor that you want to parse a XML
                          that contains multiple EntityDescriptor.
        :type entity_id: string

        :returns: settings dict with extracted data
        :rtype: dict
        """
        data = {}

        dom = OneLogin_Saml2_XML.to_etree(idp_metadata)
        idp_entity_id = want_authn_requests_signed = idp_name_id_format = idp_sso_url = idp_slo_url = certs = None

        entity_desc_path = '//md:EntityDescriptor'
        if entity_id:
            entity_desc_path += "[@entityID='%s']" % entity_id
        entity_descriptor_nodes = OneLogin_Saml2_XML.query(
            dom, entity_desc_path)

        if len(entity_descriptor_nodes) > 0:
            entity_descriptor_node = entity_descriptor_nodes[0]
            idp_descriptor_nodes = OneLogin_Saml2_XML.query(
                entity_descriptor_node, './md:IDPSSODescriptor')
            if len(idp_descriptor_nodes) > 0:
                idp_descriptor_node = idp_descriptor_nodes[0]

                idp_entity_id = entity_descriptor_node.get('entityID', None)

                want_authn_requests_signed = entity_descriptor_node.get(
                    'WantAuthnRequestsSigned', None)

                name_id_format_nodes = OneLogin_Saml2_XML.query(
                    idp_descriptor_node, './md:NameIDFormat')
                if len(name_id_format_nodes) > 0:
                    idp_name_id_format = OneLogin_Saml2_XML.element_text(
                        name_id_format_nodes[0])

                sso_nodes = OneLogin_Saml2_XML.query(
                    idp_descriptor_node,
                    "./md:SingleSignOnService[@Binding='%s']" %
                    required_sso_binding)

                if len(sso_nodes) > 0:
                    idp_sso_url = sso_nodes[0].get('Location', None)

                slo_nodes = OneLogin_Saml2_XML.query(
                    idp_descriptor_node,
                    "./md:SingleLogoutService[@Binding='%s']" %
                    required_slo_binding)

                if len(slo_nodes) > 0:
                    idp_slo_url = slo_nodes[0].get('Location', None)

                signing_nodes = OneLogin_Saml2_XML.query(
                    idp_descriptor_node,
                    "./md:KeyDescriptor[not(contains(@use, 'encryption'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate"
                )
                encryption_nodes = OneLogin_Saml2_XML.query(
                    idp_descriptor_node,
                    "./md:KeyDescriptor[not(contains(@use, 'signing'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate"
                )

                if len(signing_nodes) > 0 or len(encryption_nodes) > 0:
                    certs = {}
                    if len(signing_nodes) > 0:
                        certs['signing'] = []
                        for cert_node in signing_nodes:
                            certs['signing'].append(''.join(
                                OneLogin_Saml2_XML.element_text(
                                    cert_node).split()))
                    if len(encryption_nodes) > 0:
                        certs['encryption'] = []
                        for cert_node in encryption_nodes:
                            certs['encryption'].append(''.join(
                                OneLogin_Saml2_XML.element_text(
                                    cert_node).split()))

                data['idp'] = {}

                if idp_entity_id is not None:
                    data['idp']['entityId'] = idp_entity_id

                if idp_sso_url is not None:
                    data['idp']['singleSignOnService'] = {}
                    data['idp']['singleSignOnService']['url'] = idp_sso_url
                    data['idp']['singleSignOnService'][
                        'binding'] = required_sso_binding

                if idp_slo_url is not None:
                    data['idp']['singleLogoutService'] = {}
                    data['idp']['singleLogoutService']['url'] = idp_slo_url
                    data['idp']['singleLogoutService'][
                        'binding'] = required_slo_binding

                if want_authn_requests_signed is not None:
                    data['security'] = {}
                    data['security'][
                        'authnRequestsSigned'] = want_authn_requests_signed

                if idp_name_id_format:
                    data['sp'] = {}
                    data['sp']['NameIDFormat'] = idp_name_id_format

                if certs is not None:
                    if (len(certs) == 1 and
                        (('signing' in certs and len(certs['signing']) == 1) or
                         ('encryption' in certs and len(certs['encryption']) == 1))) or \
                        (('signing' in certs and len(certs['signing']) == 1) and
                         ('encryption' in certs and len(certs['encryption']) == 1 and
                         certs['signing'][0] == certs['encryption'][0])):
                        if 'signing' in certs:
                            data['idp']['x509cert'] = certs['signing'][0]
                        else:
                            data['idp']['x509cert'] = certs['encryption'][0]
                    else:
                        data['idp']['x509certMulti'] = certs
        return data
예제 #12
0
파일: utils.py 프로젝트: corey-ryan/opensso
    def validate_metadata_sign(xml,
                               cert=None,
                               fingerprint=None,
                               fingerprintalg='sha1',
                               validatecert=False,
                               debug=False):
        """
        Validates a signature of a EntityDescriptor.

        :param xml: The element we should validate
        :type: string | 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
        """
        if xml is None or xml == '':
            raise Exception('Empty string supplied as input')

        elem = OneLogin_Saml2_XML.to_etree(xml)
        xmlsec.enable_debug_trace(debug)
        xmlsec.tree.add_ids(elem, ["ID"])

        signature_nodes = OneLogin_Saml2_XML.query(
            elem, '/md:EntitiesDescriptor/ds:Signature')

        if len(signature_nodes) == 0:
            signature_nodes += OneLogin_Saml2_XML.query(
                elem, '/md:EntityDescriptor/ds:Signature')

            if len(signature_nodes) == 0:
                signature_nodes += OneLogin_Saml2_XML.query(
                    elem,
                    '/md:EntityDescriptor/md:SPSSODescriptor/ds:Signature')
                signature_nodes += OneLogin_Saml2_XML.query(
                    elem,
                    '/md:EntityDescriptor/md:IDPSSODescriptor/ds:Signature')

        if len(signature_nodes) > 0:
            for signature_node in signature_nodes:
                # Raises expection if invalid
                OneLogin_Saml2_Utils.validate_node_sign(signature_node,
                                                        elem,
                                                        cert,
                                                        fingerprint,
                                                        fingerprintalg,
                                                        validatecert,
                                                        debug,
                                                        raise_exceptions=True)
            return True
        else:
            raise Exception(
                'Could not validate metadata signature: No signature nodes found.'
            )
예제 #13
0
파일: utils.py 프로젝트: corey-ryan/opensso
    def validate_sign(xml,
                      cert=None,
                      fingerprint=None,
                      fingerprintalg='sha1',
                      validatecert=False,
                      debug=False,
                      xpath=None,
                      multicerts=None):
        """
        Validates a signature (Message or Assertion).

        :param xml: The element we should validate
        :type: string | 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 xpath: The xpath of the signed element
        :type: string

        :param multicerts: Multiple public certs
        :type: list

        :param raise_exceptions: Whether to return false on failure or raise an exception
        :type raise_exceptions: Boolean
        """
        if xml is None or xml == '':
            raise Exception('Empty string supplied as input')

        elem = OneLogin_Saml2_XML.to_etree(xml)
        xmlsec.enable_debug_trace(debug)
        xmlsec.tree.add_ids(elem, ["ID"])

        if xpath:
            signature_nodes = OneLogin_Saml2_XML.query(elem, xpath)
        else:
            signature_nodes = OneLogin_Saml2_XML.query(
                elem, OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH)

            if len(signature_nodes) == 0:
                signature_nodes = OneLogin_Saml2_XML.query(
                    elem, OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH)

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

            if not multicerts:
                return OneLogin_Saml2_Utils.validate_node_sign(
                    signature_node,
                    elem,
                    cert,
                    fingerprint,
                    fingerprintalg,
                    validatecert,
                    debug,
                    raise_exceptions=True)
            else:
                # If multiple certs are provided, I may ignore cert and
                # fingerprint provided by the method and just check the
                # certs multicerts
                fingerprint = fingerprintalg = None
                for cert in multicerts:
                    if OneLogin_Saml2_Utils.validate_node_sign(
                            signature_node,
                            elem,
                            cert,
                            fingerprint,
                            fingerprintalg,
                            validatecert,
                            False,
                            raise_exceptions=False):
                        return True
                raise OneLogin_Saml2_ValidationError(
                    'Signature validation failed. SAML Response rejected.',
                    OneLogin_Saml2_ValidationError.INVALID_SIGNATURE)
        else:
            raise OneLogin_Saml2_ValidationError(
                'Expected exactly one signature node; got {}.'.format(
                    len(signature_nodes)),
                OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_SIGNATURES)
예제 #14
0
파일: utils.py 프로젝트: corey-ryan/opensso
    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')

        elem = OneLogin_Saml2_XML.to_etree(xml)

        sign_algorithm_transform_map = {
            OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.Transform.DSA_SHA1,
            OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.Transform.RSA_SHA1,
            OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.Transform.RSA_SHA256,
            OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.Transform.RSA_SHA384,
            OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.Transform.RSA_SHA512
        }
        sign_algorithm_transform = sign_algorithm_transform_map.get(
            sign_algorithm, xmlsec.Transform.RSA_SHA1)

        signature = xmlsec.template.create(elem,
                                           xmlsec.Transform.EXCL_C14N,
                                           sign_algorithm_transform,
                                           ns='ds')

        issuer = OneLogin_Saml2_XML.query(elem, '//saml:Issuer')
        if len(issuer) > 0:
            issuer = issuer[0]
            issuer.addnext(signature)
            elem_to_sign = issuer.getparent()
        else:
            entity_descriptor = OneLogin_Saml2_XML.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.enable_debug_trace(debug)
        xmlsec.tree.add_ids(elem_to_sign, ["ID"])

        digest_algorithm_transform_map = {
            OneLogin_Saml2_Constants.SHA1: xmlsec.Transform.SHA1,
            OneLogin_Saml2_Constants.SHA256: xmlsec.Transform.SHA256,
            OneLogin_Saml2_Constants.SHA384: xmlsec.Transform.SHA384,
            OneLogin_Saml2_Constants.SHA512: xmlsec.Transform.SHA512
        }
        digest_algorithm_transform = digest_algorithm_transform_map.get(
            digest_algorithm, xmlsec.Transform.SHA1)

        ref = xmlsec.template.add_reference(signature,
                                            digest_algorithm_transform,
                                            uri=elem_id)
        xmlsec.template.add_transform(ref, xmlsec.Transform.ENVELOPED)
        xmlsec.template.add_transform(ref, xmlsec.Transform.EXCL_C14N)
        key_info = xmlsec.template.ensure_key_info(signature)
        xmlsec.template.add_x509_data(key_info)

        dsig_ctx = xmlsec.SignatureContext()
        sign_key = xmlsec.Key.from_memory(key, xmlsec.KeyFormat.PEM, None)
        sign_key.load_cert_from_memory(cert, xmlsec.KeyFormat.PEM)

        dsig_ctx.key = sign_key
        dsig_ctx.sign(signature)

        return OneLogin_Saml2_XML.to_string(elem)