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 __build_signature(self, saml_data, relay_state, saml_type, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ Builds the Signature :param saml_data: The SAML Data :type saml_data: string :param relay_state: The target URL the user should be redirected to :type relay_state: string :param saml_type: The target URL the user should be redirected to :type saml_type: string SAMLRequest | SAMLResponse :param sign_algorithm: Signature algorithm method :type sign_algorithm: string """ assert saml_type in ['SAMLRequest', 'SAMLResponse'] # Load the key into the xmlsec context key = self.__settings.get_sp_key() if not key: raise OneLogin_Saml2_Error( "Trying to sign the %s but can't load the SP private key" % saml_type, OneLogin_Saml2_Error.SP_CERTS_NOT_FOUND) xmlsec.initialize() dsig_ctx = xmlsec.DSigCtx() dsig_ctx.signKey = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None) saml_data_str = '%s=%s' % (saml_type, quote_plus(saml_data)) relay_state_str = 'RelayState=%s' % quote_plus(relay_state) alg_str = 'SigAlg=%s' % quote_plus(sign_algorithm) sign_data = [saml_data_str, relay_state_str, alg_str] msg = '&'.join(sign_data) # Sign the metadata with our private key. sign_algorithm_transform_map = { OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.TransformDsaSha1, OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.TransformRsaSha1, OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.TransformRsaSha256, OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.TransformRsaSha384, OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.TransformRsaSha512 } sign_algorithm_transform = sign_algorithm_transform_map.get( sign_algorithm, xmlsec.TransformRsaSha1) signature = dsig_ctx.signBinary(str(msg), sign_algorithm_transform) return b64encode(signature)
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 __build_signature(self, saml_data, relay_state, saml_type, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1): """ Builds the Signature :param saml_data: The SAML Data :type saml_data: string :param relay_state: The target URL the user should be redirected to :type relay_state: string :param saml_type: The target URL the user should be redirected to :type saml_type: string SAMLRequest | SAMLResponse :param sign_algorithm: Signature algorithm method :type sign_algorithm: string """ assert saml_type in ['SAMLRequest', 'SAMLResponse'] # Load the key into the xmlsec context key = self.__settings.get_sp_key() if not key: raise OneLogin_Saml2_Error( "Trying to sign the %s but can't load the SP private key" % saml_type, OneLogin_Saml2_Error.SP_CERTS_NOT_FOUND ) xmlsec.initialize() dsig_ctx = xmlsec.DSigCtx() dsig_ctx.signKey = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None) saml_data_str = '%s=%s' % (saml_type, quote_plus(saml_data)) relay_state_str = 'RelayState=%s' % quote_plus(relay_state) alg_str = 'SigAlg=%s' % quote_plus(sign_algorithm) sign_data = [saml_data_str, relay_state_str, alg_str] msg = '&'.join(sign_data) # Sign the metadata with our private key. sign_algorithm_transform_map = { OneLogin_Saml2_Constants.DSA_SHA1: xmlsec.TransformDsaSha1, OneLogin_Saml2_Constants.RSA_SHA1: xmlsec.TransformRsaSha1, OneLogin_Saml2_Constants.RSA_SHA256: xmlsec.TransformRsaSha256, OneLogin_Saml2_Constants.RSA_SHA384: xmlsec.TransformRsaSha384, OneLogin_Saml2_Constants.RSA_SHA512: xmlsec.TransformRsaSha512 } sign_algorithm_transform = sign_algorithm_transform_map.get(sign_algorithm, xmlsec.TransformRsaSha1) signature = dsig_ctx.signBinary(str(msg), sign_algorithm_transform) return b64encode(signature)
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 get_response_xml(parameters, signed=False): """ Returns XML for response, with signatures, if signed is True. """ # Reset signatures. params = {} params.update(parameters) params["RESPONSE_SIGNATURE"] = "" _get_in_response_to(params) template = string.Template(RESPONSE) unsigned = template.substitute(params) logging.debug("Unsigned:") logging.debug(unsigned) if not signed: return unsigned # Sign it. # signature_xml = get_signature_xml(unsigned, params['RESPONSE_ID']) # params['RESPONSE_SIGNATURE'] = signature_xml # signed = template.substitute(params) # # logging.debug('Signed:') # logging.debug(signed) config = saml2idp_metadata.SAML2IDP_CONFIG private_key_file = config["private_key_file"] certificate_file = config["certificate_file"] doc = fromstring(unsigned) xmlsec.initialize() signature = Signature(xmlsec.TransformExclC14N, xmlsec.TransformRsaSha1) doc.insert(0, signature) ref = signature.addReference(xmlsec.TransformSha1) ref.addTransform(xmlsec.TransformEnveloped) key_info = signature.ensureKeyInfo() key_info.addKeyName() key_info.addX509Data() dsigCtx = xmlsec.DSigCtx() signKey = xmlsec.Key.load(private_key_file, xmlsec.KeyDataFormatPem, None) signKey.loadCert(certificate_file, xmlsec.KeyDataFormatPem) dsigCtx.signKey = signKey dsigCtx.sign(signature) return tostring(doc)
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
''' Created on 1 Feb 2013 @author: Kristy Siu ''' import logging import urlparse import sys import uuid from keystone import exception sys.path.insert(0, '../') import dm.xmlsec.binding as xmlsec xmlsec.initialize() from os.path import dirname, basename from lxml.etree import parse,tostring,fromstring,ElementTree from time import localtime, strftime, gmtime import urllib import webbrowser import urllib2 import zlib import base64 import webob.dec import webob.exc import json from keystone.contrib import mapping from keystone import catalog LOG = logging.getLogger(__name__)
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_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 _encrypt_assertion(unencrypted): # load the rsa key # Create and initialize keys manager, we use a simple list based # keys manager, implement your own KeysStore klass if you need # something more sophisticated # mngr = xmlsec.KeysMngr() # config = saml2idp_metadata.SAML2IDP_CONFIG # key = xmlsec.cryptoAppKeyLoad(config['public_key_file'], xmlsec.KeyDataFormatPem, None, None, None) # key.setName(config['public_key_file']) # # add the key to the manager # xmlsec.cryptoAppDefaultKeysMngrAdoptKey(mngr, key) # # # now encrypt the xml # doc = libxml2.parseDoc(unencrypted) # # Create encryption template to encrypt XML file and replace # # its content with encryption result # enc_data_node = xmlsec.TmplEncData(doc, xmlsec.transformAes128CbcId(), None, xmlsec.TypeEncElement, None, None) # # put encrypted data in the <enc:CipherValue/> node # enc_data_node.ensureCipherValue() # # add <dsig:KeyInfo/> # key_info_node = enc_data_node.ensureKeyInfo(None) # # Add <enc:EncryptedKey/> to store the encrypted session key # enc_key_node = key_info_node.addEncryptedKey(xmlsec.transformRsaPkcs1Id(), None, None, None) # # put encrypted key in the <enc:CipherValue/> node # enc_key_node.ensureCipherValue() # # Add <dsig:KeyInfo/> and <dsig:KeyName/> nodes to <enc:EncryptedKey/> # key_info_node2 = enc_key_node.ensureKeyInfo(None) # # Set key name so we can lookup key when needed # key_info_node2.addKeyName(config['public_key_file']) # # Create encryption context # enc_ctx = xmlsec.EncCtx(mngr) # # Generate a Triple DES key # key = xmlsec.keyGenerate(xmlsec.keyDataDesId(), 192, xmlsec.KeyDataTypeSession) # enc_ctx.encKey = key # # Encrypt the data # enc_ctx.xmlEncrypt(enc_data_node, doc.getRootElement()) # # # Destroy all # key.destroy() # mngr.destroy() # enc_ctx.destroy() # enc_data_node.freeNode() # # doc.freeDoc() # return doc xmlsec.initialize() config = saml2idp_metadata.SAML2IDP_CONFIG mngr = xmlsec.KeysMngr() key = xmlsec.Key.load(config["public_key_file"], xmlsec.KeyDataFormatPem) key.name = basename(config["public_key_file"]) mngr.addKey(key) doc = fromstring(unencrypted) encData = EncData(xmlsec.TransformDes3Cbc, type=xmlsec.TypeEncElement) encData.ensureCipherValue() # target for encryption result keyInfo = encData.ensureKeyInfo() encKey = keyInfo.addEncryptedKey(xmlsec.TransformRsaPkcs1) encKey.ensureCipherValue() encKeyInfo = encKey.ensureKeyInfo() encKeyInfo.addKeyName(key.name) encCtx = xmlsec.EncCtx(mngr) encCtx.encKey = xmlsec.Key.generate(xmlsec.KeyDataDes, 192, xmlsec.KeyDataTypeSession) ed = encCtx.encryptXml(encData, doc) return tostring(ed.getroottree())
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(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() #xmlsec.base_64_set_default_line_size(0) 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)) 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_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 generate_name_id(value, sp_nq, sp_format, 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) 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)
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)
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_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_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): """ 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_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
"""Initialize xmlsec bindings.""" from zope.i18nmessageid import MessageFactory _ = MessageFactory('quintagroup.xmlsec.init') def initialize(context): """Initializer called when used as a Zope 2 product.""" from dm.xmlsec.binding import initialize; initialize()
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) # 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
from urllib import quote_plus from uuid import uuid4 from xml.dom.minidom import Document, Element from defusedxml.minidom import parseString from functools import wraps import zlib import dm.xmlsec.binding as xmlsec from dm.xmlsec.binding.tmpl import EncData, Signature from onelogin.saml2.constants import OneLogin_Saml2_Constants from onelogin.saml2.errors import OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError if not globals().get('xmlsec_setup', False): xmlsec.initialize() globals()['xmlsec_setup'] = True def return_false_on_exception(func): """ Decorator. When applied to a function, it will, by default, suppress any exceptions raised by that function and return False. It may be overridden by passing a "raise_exceptions" keyword argument when calling the wrapped function. """ @wraps(func) def exceptfalse(*args, **kwargs): if not kwargs.pop('raise_exceptions', False): try: return func(*args, **kwargs) except Exception:
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)