def decrypt_element(encrypted_data, key, debug=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 :returns: The decrypted element. :rtype: lxml.etree.Element """ if isinstance(encrypted_data, Element): encrypted_data = fromstring(str(encrypted_data.toxml())) elif isinstance(encrypted_data, basestring): encrypted_data = fromstring(str(encrypted_data)) if debug: xmlsec.set_error_callback(print_xmlsec_errors) mngr = xmlsec.KeysMngr() key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None) mngr.addKey(key) enc_ctx = xmlsec.EncCtx(mngr) return enc_ctx.decrypt(encrypted_data)
def decrypt_element(encrypted_data, key, debug=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 :returns: The decrypted element. :rtype: lxml.etree.Element """ if isinstance(encrypted_data, Element): encrypted_data = fromstring(str(encrypted_data.toxml())) elif isinstance(encrypted_data, basestring): encrypted_data = fromstring(str(encrypted_data)) xmlsec.initialize() if debug: xmlsec.set_error_callback(print_xmlsec_errors) mngr = xmlsec.KeysMngr() key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None) mngr.addKey(key) enc_ctx = xmlsec.EncCtx(mngr) return enc_ctx.decrypt(encrypted_data)
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
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
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
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
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 = fromstring(str(encrypted_data.toxml()), forbid_dtd=True) elif isinstance(encrypted_data, basestring): encrypted_data = fromstring(str(encrypted_data), forbid_dtd=True) elif not inplace and isinstance(encrypted_data, etree._Element): encrypted_data = deepcopy(encrypted_data) error_callback_method = None if debug: error_callback_method = print_xmlsec_errors xmlsec.set_error_callback(error_callback_method) mngr = xmlsec.KeysMngr() key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None) mngr.addKey(key) enc_ctx = xmlsec.EncCtx(mngr) return enc_ctx.decrypt(encrypted_data)
def validate_sign( xml, cert=None, fingerprint=None, fingerprintalg="sha1", validatecert=False, debug=False, xpath=None ): """ 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 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 """ 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(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") if debug: xmlsec.set_error_callback(print_xmlsec_errors) xmlsec.addIDs(elem, ["ID"]) if xpath: signature_nodes = OneLogin_Saml2_Utils.query(elem, xpath) else: signature_nodes = OneLogin_Saml2_Utils.query(elem, OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH) if len(signature_nodes) == 0: signature_nodes = OneLogin_Saml2_Utils.query(elem, OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH) if len(signature_nodes) == 1: signature_node = signature_nodes[0] return OneLogin_Saml2_Utils.validate_node_sign( signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, debug ) else: return False except Exception: return False
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 = OneLogin_Saml2_Utils.element_text(x509_certificate_node) x509_cert_value_formatted = OneLogin_Saml2_Utils.format_cert(x509_cert_value) x509_fingerprint_value = OneLogin_Saml2_Utils.calculate_x509_fingerprint(x509_cert_value_formatted, fingerprintalg) if fingerprint == x509_fingerprint_value: cert = x509_cert_value_formatted # 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
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
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)
def generate_name_id(value, sp_nq, sp_format, cert=None, debug=False): """ Generates a nameID. :param value: fingerprint :type: string :param sp_nq: SP Name Qualifier :type: string :param sp_format: SP Format :type: string :param cert: IdP Public Cert to encrypt the nameID :type: string :param debug: Activate the xmlsec debug :type: bool :returns: DOMElement | XMLSec nameID :rtype: string """ doc = Document() name_id_container = doc.createElementNS(OneLogin_Saml2_Constants.NS_SAML, 'container') name_id_container.setAttribute("xmlns:saml", OneLogin_Saml2_Constants.NS_SAML) name_id = doc.createElement('saml:NameID') name_id.setAttribute('SPNameQualifier', sp_nq) name_id.setAttribute('Format', sp_format) name_id.appendChild(doc.createTextNode(value)) name_id_container.appendChild(name_id) if cert is not None: xml = name_id_container.toxml() elem = fromstring(xml) xmlsec.initialize() if debug: xmlsec.set_error_callback(print_xmlsec_errors) # Load the public cert mngr = xmlsec.KeysMngr() file_cert = OneLogin_Saml2_Utils.write_temp_file(cert) key_data = xmlsec.Key.load(file_cert.name, xmlsec.KeyDataFormatCertPem, None) key_data.name = basename(file_cert.name) mngr.addKey(key_data) file_cert.close() # Prepare for encryption enc_data = EncData(xmlsec.TransformAes128Cbc, type=xmlsec.TypeEncElement) enc_data.ensureCipherValue() key_info = enc_data.ensureKeyInfo() # enc_key = key_info.addEncryptedKey(xmlsec.TransformRsaPkcs1) enc_key = key_info.addEncryptedKey(xmlsec.TransformRsaOaep) enc_key.ensureCipherValue() # Encrypt! enc_ctx = xmlsec.EncCtx(mngr) enc_ctx.encKey = xmlsec.Key.generate(xmlsec.KeyDataAes, 128, xmlsec.KeyDataTypeSession) edata = enc_ctx.encryptXml(enc_data, elem[0]) newdoc = parseString(etree.tostring(edata)) if newdoc.hasChildNodes(): child = newdoc.firstChild child.removeAttribute('xmlns') child.removeAttribute('xmlns:saml') child.setAttribute('xmlns:xenc', OneLogin_Saml2_Constants.NS_XENC) child.setAttribute('xmlns:dsig', OneLogin_Saml2_Constants.NS_DS) nodes = newdoc.getElementsByTagName("*") for node in nodes: if node.tagName == 'ns0:KeyInfo': node.tagName = 'dsig:KeyInfo' node.removeAttribute('xmlns:ns0') node.setAttribute('xmlns:dsig', OneLogin_Saml2_Constants.NS_DS) else: node.tagName = 'xenc:' + node.tagName encrypted_id = newdoc.createElement('saml:EncryptedID') encrypted_data = newdoc.replaceChild(encrypted_id, newdoc.firstChild) encrypted_id.appendChild(encrypted_data) return newdoc.saveXML(encrypted_id) else: return doc.saveXML(name_id)
info.append('obj=' + errorObject) if errorSubject != 'unknown': info.append('subject=' + errorSubject) if msg.strip(): info.append('msg=' + msg) if info: logger.debug('%s:%d(%s)' % (filename, line, func), ' '.join(info)) class CertificationError(Exception): pass # Initialize the xmlsec library xmlsec.initialize() xmlsec.set_error_callback(log_errors) class SignQueue(object): WSU_ID = ns_id('Id', ns.wsuns) DS_DIGEST_VALUE = ns_id('DigestValue', ns.dsns) DS_REFERENCE = ns_id('Reference', ns.dsns) DS_TRANSFORMS = ns_id('Transforms', ns.dsns) def __init__(self): self.queue = [] def push_and_mark(self, element): unique_id = get_unique_id() element.set(self.WSU_ID, unique_id) self.queue.append(unique_id)
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')
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 pubic 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') elif isinstance(xml, etree._Element): elem = xml elif isinstance(xml, Document): xml = xml.toxml() elem = fromstring(str(xml), 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(str(xml), forbid_dtd=True) elif isinstance(xml, basestring): elem = fromstring(str(xml), 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) xmlsec.addIDs(elem, ["ID"]) if xpath: signature_nodes = OneLogin_Saml2_Utils.query(elem, xpath) else: signature_nodes = OneLogin_Saml2_Utils.query(elem, OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH) if len(signature_nodes) == 0: signature_nodes = OneLogin_Saml2_Utils.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.') else: raise OneLogin_Saml2_ValidationError('Expected exactly one signature node; got {}.'.format(len(signature_nodes)), OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_SIGNATURES)
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)
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 pubic 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: xmlsec.initialize() 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) if cert is None or cert == "": return False # 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")) 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 except Exception: return False
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
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')
def validate_sign(xml, cert=None, fingerprint=None, fingerprintalg="sha1", 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 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 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, "/samlp:Response/ds:Signature") if not len(signature_nodes) > 0: signature_nodes += OneLogin_Saml2_Utils.query( elem, "/samlp:Response/saml:EncryptedAssertion/saml:Assertion/ds:Signature" ) signature_nodes += OneLogin_Saml2_Utils.query(elem, "/samlp:Response/saml:Assertion/ds:Signature") if len(signature_nodes) == 1: signature_node = signature_nodes[0] return OneLogin_Saml2_Utils.validate_node_sign( signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, debug ) else: return False except Exception: return False
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
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 pubic 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 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_MD), "xmlns:md", unicode(OneLogin_Saml2_Constants.NS_MD) ) 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, "/md:EntitiesDescriptor/ds:Signature") if len(signature_nodes) == 0: signature_nodes += OneLogin_Saml2_Utils.query(elem, "/md:EntityDescriptor/ds:Signature") if len(signature_nodes) == 0: signature_nodes += OneLogin_Saml2_Utils.query( elem, "/md:EntityDescriptor/md:SPSSODescriptor/ds:Signature" ) signature_nodes += OneLogin_Saml2_Utils.query( elem, "/md:EntityDescriptor/md:IDPSSODescriptor/ds:Signature" ) if len(signature_nodes) > 0: for signature_node in signature_nodes: if not OneLogin_Saml2_Utils.validate_node_sign( signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, debug ): return False return True else: return False except Exception: return False
def validate_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha1', validatecert=False, debug=False, xpath=None): """ 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 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 """ 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(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') if debug: xmlsec.set_error_callback(print_xmlsec_errors) xmlsec.addIDs(elem, ["ID"]) if xpath: signature_nodes = OneLogin_Saml2_Utils.query(elem, xpath) else: signature_nodes = OneLogin_Saml2_Utils.query( elem, OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH) if len(signature_nodes) == 0: signature_nodes = OneLogin_Saml2_Utils.query( elem, OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH) if len(signature_nodes) == 1: signature_node = signature_nodes[0] return OneLogin_Saml2_Utils.validate_node_sign( signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, debug) else: return False except Exception: return False
def validate_sign(xml, cert=None, fingerprint=None, fingerprintalg='sha1', 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 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 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, '/samlp:Response/ds:Signature') if not len(signature_nodes) > 0: signature_nodes += OneLogin_Saml2_Utils.query( elem, '/samlp:Response/saml:EncryptedAssertion/saml:Assertion/ds:Signature' ) signature_nodes += OneLogin_Saml2_Utils.query( elem, '/samlp:Response/saml:Assertion/ds:Signature') if len(signature_nodes) == 1: signature_node = signature_nodes[0] return OneLogin_Saml2_Utils.validate_node_sign( signature_node, elem, cert, fingerprint, fingerprintalg, validatecert, debug) else: return False except Exception: return False
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)
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)
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 pubic 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') 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_MD), 'xmlns:md', unicode(OneLogin_Saml2_Constants.NS_MD)) xml = xml.toxml() elem = fromstring(str(xml)) elif isinstance(xml, basestring): elem = fromstring(str(xml)) 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) xmlsec.addIDs(elem, ["ID"]) signature_nodes = OneLogin_Saml2_Utils.query( elem, '/md:EntitiesDescriptor/ds:Signature') if len(signature_nodes) == 0: signature_nodes += OneLogin_Saml2_Utils.query( elem, '/md:EntityDescriptor/ds:Signature') if len(signature_nodes) == 0: signature_nodes += OneLogin_Saml2_Utils.query( elem, '/md:EntityDescriptor/md:SPSSODescriptor/ds:Signature') signature_nodes += OneLogin_Saml2_Utils.query( elem, '/md:EntityDescriptor/md:IDPSSODescriptor/ds:Signature') if len(signature_nodes) > 0: for signature_node in signature_nodes: 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.' )
def generate_name_id(value, sp_nq, sp_format=None, cert=None, debug=False, nq=None): """ Generates a nameID. :param value: fingerprint :type: string :param sp_nq: SP Name Qualifier :type: string :param sp_format: SP Format :type: string :param cert: IdP Public Cert to encrypt the nameID :type: string :param debug: Activate the xmlsec debug :type: bool :param nq: IDP Name Qualifier :type: string :returns: DOMElement | XMLSec nameID :rtype: string """ doc = Document() name_id_container = doc.createElementNS( OneLogin_Saml2_Constants.NS_SAML, 'container') name_id_container.setAttribute("xmlns:saml", OneLogin_Saml2_Constants.NS_SAML) name_id = doc.createElement('saml:NameID') if sp_nq is not None: name_id.setAttribute('SPNameQualifier', sp_nq) if nq is not None: name_id.setAttribute('NameQualifier', nq) if sp_format is not None: name_id.setAttribute('Format', sp_format) name_id.appendChild(doc.createTextNode(value)) name_id_container.appendChild(name_id) if cert is not None: xml = name_id_container.toxml() elem = fromstring(xml) error_callback_method = None if debug: error_callback_method = print_xmlsec_errors xmlsec.set_error_callback(error_callback_method) # Load the public cert mngr = xmlsec.KeysMngr() file_cert = OneLogin_Saml2_Utils.write_temp_file(cert) key_data = xmlsec.Key.load(file_cert.name, xmlsec.KeyDataFormatCertPem, None) key_data.name = basename(file_cert.name) mngr.addKey(key_data) file_cert.close() # Prepare for encryption enc_data = EncData(xmlsec.TransformAes128Cbc, type=xmlsec.TypeEncElement) enc_data.ensureCipherValue() key_info = enc_data.ensureKeyInfo() # enc_key = key_info.addEncryptedKey(xmlsec.TransformRsaPkcs1) enc_key = key_info.addEncryptedKey(xmlsec.TransformRsaOaep) enc_key.ensureCipherValue() # Encrypt! enc_ctx = xmlsec.EncCtx(mngr) enc_ctx.encKey = xmlsec.Key.generate(xmlsec.KeyDataAes, 128, xmlsec.KeyDataTypeSession) edata = enc_ctx.encryptXml(enc_data, elem[0]) newdoc = parseString( tostring(edata, encoding='unicode').encode('utf-8')) if newdoc.hasChildNodes(): child = newdoc.firstChild child.removeAttribute('xmlns') child.removeAttribute('xmlns:saml') child.setAttribute('xmlns:xenc', OneLogin_Saml2_Constants.NS_XENC) child.setAttribute('xmlns:dsig', OneLogin_Saml2_Constants.NS_DS) nodes = newdoc.getElementsByTagName("*") for node in nodes: if node.tagName == 'ns0:KeyInfo': node.tagName = 'dsig:KeyInfo' node.removeAttribute('xmlns:ns0') node.setAttribute('xmlns:dsig', OneLogin_Saml2_Constants.NS_DS) else: node.tagName = 'xenc:' + node.tagName encrypted_id = newdoc.createElement('saml:EncryptedID') encrypted_data = newdoc.replaceChild(encrypted_id, newdoc.firstChild) encrypted_id.appendChild(encrypted_data) return newdoc.saveXML(encrypted_id) else: return doc.saveXML(name_id)
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 pubic 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') elif isinstance(xml, etree._Element): elem = xml elif isinstance(xml, Document): xml = xml.toxml() elem = fromstring(str(xml), forbid_dtd=True) elif isinstance(xml, Element): xml.setAttributeNS( unicode(OneLogin_Saml2_Constants.NS_MD), 'xmlns:md', unicode(OneLogin_Saml2_Constants.NS_MD) ) xml = xml.toxml() elem = fromstring(str(xml), forbid_dtd=True) elif isinstance(xml, basestring): elem = fromstring(str(xml), 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) xmlsec.addIDs(elem, ["ID"]) signature_nodes = OneLogin_Saml2_Utils.query(elem, '/md:EntitiesDescriptor/ds:Signature') if len(signature_nodes) == 0: signature_nodes += OneLogin_Saml2_Utils.query(elem, '/md:EntityDescriptor/ds:Signature') if len(signature_nodes) == 0: signature_nodes += OneLogin_Saml2_Utils.query(elem, '/md:EntityDescriptor/md:SPSSODescriptor/ds:Signature') signature_nodes += OneLogin_Saml2_Utils.query(elem, '/md:EntityDescriptor/md:IDPSSODescriptor/ds:Signature') if len(signature_nodes) > 0: for signature_node in signature_nodes: 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.')
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 pubic 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') 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') error_callback_method = None if debug: error_callback_method = print_xmlsec_errors xmlsec.set_error_callback(error_callback_method) xmlsec.addIDs(elem, ["ID"]) if xpath: signature_nodes = OneLogin_Saml2_Utils.query(elem, xpath) else: signature_nodes = OneLogin_Saml2_Utils.query( elem, OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH) if len(signature_nodes) == 0: signature_nodes = OneLogin_Saml2_Utils.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.') else: raise OneLogin_Saml2_ValidationError( 'Expected exactly one signature node; got {}.'.format( len(signature_nodes)), OneLogin_Saml2_ValidationError.WRONG_NUMBER_OF_SIGNATURES)
def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constants.RSA_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 """ 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") if debug: xmlsec.set_error_callback(print_xmlsec_errors) # 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: 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, 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)