def sign_envelope(envelope, key_file): """Sign the given soap request with the given key""" doc = etree.fromstring(envelope) body = get_body(doc) queue = SignQueue() queue.push_and_mark(body) security_node = ensure_security_header(doc, queue) security_token_node = create_binary_security_token(key_file) queue.push_and_mark(security_token_node) signature_node = Signature(xmlsec.TransformExclC14N, xmlsec.TransformRsaSha1) security_node.append(security_token_node) security_node.append(signature_node) queue.insert_references(signature_node) key_info = create_key_info_node(security_token_node) signature_node.append(key_info) # Sign the generated xml xmlsec.addIDs(doc, ['Id']) dsigCtx = xmlsec.DSigCtx() dsigCtx.signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None) dsigCtx.sign(signature_node) return etree.tostring(doc)
def sign_envelope(envelope, key_file, add_to_queue=None): """Sign the given soap request body with the given key. An optional add_to_queue callable can be passed to add additional elements to the signing queue. This function gets passed the document tree and should return a collection of Elements.""" doc = etree.fromstring(envelope) body = get_body(doc) queue = SignQueue() queue.push_and_mark(body) if add_to_queue: if not hasattr(add_to_queue, '__call__'): raise ValueError('`zadd_to_queue` kwarg must be a callable') extra_sign_queue = add_to_queue(doc) if not hasattr(extra_sign_queue, '__iter__'): raise ValueError('`add_to_queue` must return an iterable value') for el in extra_sign_queue: queue.push_and_mark(el) security_node = ensure_security_header(doc, queue) security_token_node = create_binary_security_token(key_file) signature_node = Signature(xmlsec.TransformExclC14N, xmlsec.TransformRsaSha1) security_node.append(security_token_node) security_node.append(signature_node) queue.insert_references(signature_node) key_info = create_key_info_node(security_token_node) signature_node.append(key_info) # Sign the generated xml xmlsec.addIDs(doc, ['Id']) dsigCtx = xmlsec.DSigCtx() dsigCtx.signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None) dsigCtx.sign(signature_node) return etree.tostring(doc)
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 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(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 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')