From f4169837c128cf64f3aa439ee9fa8833480b3139 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Wed, 24 Dec 2014 20:13:03 -0800 Subject: [PATCH 01/10] Add support for SingleSignOnService with HTTP-POST binding Warning, this changes the return type of `prepare_for_authentication` by including the chosen binding, and opens the door for supporting other SingleSignOnService bindings. --- src/saml2/client.py | 33 ++++++++++++++++++++++----------- src/saml2/client_base.py | 4 ++++ tests/test_51_client.py | 7 +++++-- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/saml2/client.py b/src/saml2/client.py index d64bd80..ebd744a 100644 --- a/src/saml2/client.py +++ b/src/saml2/client.py @@ -42,7 +42,7 @@ class Saml2Client(Base): """ The basic pySAML2 service provider class """ def prepare_for_authenticate(self, entityid=None, relay_state="", - binding=saml2.BINDING_HTTP_REDIRECT, vorg="", + binding=None, vorg="", nameid_format=None, scoping=None, consent=None, extensions=None, sign=None, @@ -64,20 +64,31 @@ class Saml2Client(Base): :return: session id and AuthnRequest info """ - destination = self._sso_location(entityid, binding) + expected_binding = binding - reqid, req = self.create_authn_request(destination, vorg, scoping, - response_binding, nameid_format, - consent=consent, - extensions=extensions, sign=sign, - **kwargs) - _req_str = "%s" % req + for binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]: + if expected_binding and binding != expected_binding: + continue - logger.info("AuthNReq: %s" % _req_str) + destination = self._sso_location(entityid, binding) + logger.info("destination to provider: %s" % destination) - info = self.apply_binding(binding, _req_str, destination, relay_state) + reqid, request = self.create_authn_request( + destination, vorg, scoping, response_binding, nameid_format, + consent=consent, + extensions=extensions, sign=sign, + **kwargs) - return reqid, info + _req_str = str(request) + + logger.info("AuthNReq: %s" % _req_str) + + http_info = self.apply_binding(binding, _req_str, destination, + relay_state) + + return reqid, binding, http_info + else: + raise SignonError("No binding available for singon") def global_logout(self, name_id, reason="", expire=None, sign=None): """ More or less a layer of indirection :-/ diff --git a/src/saml2/client_base.py b/src/saml2/client_base.py index de43387..4f6c20c 100644 --- a/src/saml2/client_base.py +++ b/src/saml2/client_base.py @@ -71,6 +71,10 @@ class VerifyError(SAMLError): pass +class SignonError(SAMLError): + pass + + class LogoutError(SAMLError): pass diff --git a/tests/test_51_client.py b/tests/test_51_client.py index a82a278..eebbccf 100644 --- a/tests/test_51_client.py +++ b/tests/test_51_client.py @@ -619,10 +619,11 @@ class TestClientWithDummy(): def test_do_authn(self): binding = BINDING_HTTP_REDIRECT response_binding = BINDING_HTTP_POST - sid, http_args = self.client.prepare_for_authenticate( + sid, auth_binding, http_args = self.client.prepare_for_authenticate( IDP, "http://www.example.com/relay_state", binding=binding, response_binding=response_binding) + assert binding == auth_binding assert isinstance(sid, basestring) assert len(http_args) == 4 assert http_args["headers"][0][0] == "Location" @@ -669,11 +670,13 @@ class TestClientWithDummy(): def test_post_sso(self): binding = BINDING_HTTP_POST response_binding = BINDING_HTTP_POST - sid, http_args = self.client.prepare_for_authenticate( + sid, auth_binding, http_args = self.client.prepare_for_authenticate( "urn:mace:example.com:saml:roland:idp", relay_state="really", binding=binding, response_binding=response_binding) _dic = unpack_form(http_args["data"][3]) + assert binding == auth_binding + req = self.server.parse_authn_request(_dic["SAMLRequest"], binding) resp_args = self.server.response_args(req.message, [response_binding]) assert resp_args["binding"] == response_binding From fdbd305fb78cccc9a7a9327f970914f296d26f27 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Thu, 5 Feb 2015 10:52:37 -0800 Subject: [PATCH 02/10] Add support for SingleSignOnService negotiating which binding to use --- src/saml2/client.py | 45 +++++++++++++++++++++++++++++-- src/saml2/client_base.py | 2 +- tests/test_51_client.py | 58 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 99 insertions(+), 6 deletions(-) diff --git a/src/saml2/client.py b/src/saml2/client.py index ebd744a..6ee5ef3 100644 --- a/src/saml2/client.py +++ b/src/saml2/client.py @@ -42,7 +42,7 @@ class Saml2Client(Base): """ The basic pySAML2 service provider class """ def prepare_for_authenticate(self, entityid=None, relay_state="", - binding=None, vorg="", + binding=saml2.BINDING_HTTP_REDIRECT, vorg="", nameid_format=None, scoping=None, consent=None, extensions=None, sign=None, @@ -64,6 +64,47 @@ class Saml2Client(Base): :return: session id and AuthnRequest info """ + reqid, negotiated_binding, info = self.prepare_for_negotiated_authenticate( + entityid=entityid, + relay_state=relay_state, + binding=binding, + vorg=vorg, + nameid_format=nameid_format, + scoping=scoping, + consent=consent, + extensions=extensions, + sign=sign, + response_binding=response_binding, + **kwargs) + + assert negotiated_binding == binding + + return reqid, info + + def prepare_for_negotiated_authenticate(self, entityid=None, relay_state="", + binding=None, vorg="", + nameid_format=None, + scoping=None, consent=None, extensions=None, + sign=None, + response_binding=saml2.BINDING_HTTP_POST, + **kwargs): + """ Makes all necessary preparations for an authentication request that negotiates + which binding to use for authentication. + + :param entityid: The entity ID of the IdP to send the request to + :param relay_state: To where the user should be returned after + successfull log in. + :param binding: Which binding to use for sending the request + :param vorg: The entity_id of the virtual organization I'm a member of + :param scoping: For which IdPs this query are aimed. + :param consent: Whether the principal have given her consent + :param extensions: Possible extensions + :param sign: Whether the request should be signed or not. + :param response_binding: Which binding to use for receiving the response + :param kwargs: Extra key word arguments + :return: session id and AuthnRequest info + """ + expected_binding = binding for binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]: @@ -88,7 +129,7 @@ class Saml2Client(Base): return reqid, binding, http_info else: - raise SignonError("No binding available for singon") + raise SignOnError("No supported bindings available for authentication") def global_logout(self, name_id, reason="", expire=None, sign=None): """ More or less a layer of indirection :-/ diff --git a/src/saml2/client_base.py b/src/saml2/client_base.py index 4f6c20c..4e9cbdf 100644 --- a/src/saml2/client_base.py +++ b/src/saml2/client_base.py @@ -71,7 +71,7 @@ class VerifyError(SAMLError): pass -class SignonError(SAMLError): +class SignOnError(SAMLError): pass diff --git a/tests/test_51_client.py b/tests/test_51_client.py index eebbccf..26b9da8 100644 --- a/tests/test_51_client.py +++ b/tests/test_51_client.py @@ -619,7 +619,26 @@ class TestClientWithDummy(): def test_do_authn(self): binding = BINDING_HTTP_REDIRECT response_binding = BINDING_HTTP_POST - sid, auth_binding, http_args = self.client.prepare_for_authenticate( + sid, http_args = self.client.prepare_for_authenticate( + IDP, "http://www.example.com/relay_state", + binding=binding, response_binding=response_binding) + + assert isinstance(sid, basestring) + assert len(http_args) == 4 + assert http_args["headers"][0][0] == "Location" + assert http_args["data"] == [] + redirect_url = http_args["headers"][0][1] + _, _, _, _, qs, _ = urlparse.urlparse(redirect_url) + qs_dict = urlparse.parse_qs(qs) + req = self.server.parse_authn_request(qs_dict["SAMLRequest"][0], + binding) + resp_args = self.server.response_args(req.message, [response_binding]) + assert resp_args["binding"] == response_binding + + def test_do_negotiated_authn(self): + binding = BINDING_HTTP_REDIRECT + response_binding = BINDING_HTTP_POST + sid, auth_binding, http_args = self.client.prepare_for_negotiated_authenticate( IDP, "http://www.example.com/relay_state", binding=binding, response_binding=response_binding) @@ -670,7 +689,40 @@ class TestClientWithDummy(): def test_post_sso(self): binding = BINDING_HTTP_POST response_binding = BINDING_HTTP_POST - sid, auth_binding, http_args = self.client.prepare_for_authenticate( + sid, http_args = self.client.prepare_for_authenticate( + "urn:mace:example.com:saml:roland:idp", relay_state="really", + binding=binding, response_binding=response_binding) + _dic = unpack_form(http_args["data"][3]) + + req = self.server.parse_authn_request(_dic["SAMLRequest"], binding) + resp_args = self.server.response_args(req.message, [response_binding]) + assert resp_args["binding"] == response_binding + + # Normally a response would now be sent back to the users web client + # Here I fake what the client will do + # create the form post + + http_args["data"] = urllib.urlencode(_dic) + http_args["method"] = "POST" + http_args["dummy"] = _dic["SAMLRequest"] + http_args["headers"] = [('Content-type', + 'application/x-www-form-urlencoded')] + + response = self.client.send(**http_args) + print response.text + _dic = unpack_form(response.text[3], "SAMLResponse") + resp = self.client.parse_authn_request_response(_dic["SAMLResponse"], + BINDING_HTTP_POST, + {sid: "/"}) + ac = resp.assertion.authn_statement[0].authn_context + assert ac.authenticating_authority[0].text == \ + 'http://www.example.com/login' + assert ac.authn_context_class_ref.text == INTERNETPROTOCOLPASSWORD + + def test_negotiated_post_sso(self): + binding = BINDING_HTTP_POST + response_binding = BINDING_HTTP_POST + sid, auth_binding, http_args = self.client.prepare_for_negotiated_authenticate( "urn:mace:example.com:saml:roland:idp", relay_state="really", binding=binding, response_binding=response_binding) _dic = unpack_form(http_args["data"][3]) @@ -711,4 +763,4 @@ class TestClientWithDummy(): if __name__ == "__main__": tc = TestClient() tc.setup_class() - tc.test_sign_then_encrypt_assertion_advice() \ No newline at end of file + tc.test_sign_then_encrypt_assertion_advice() From 805915bce5d124f3e0bb45aab0ccd0f7fe16651b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Ho=CC=88rberg?= Date: Fri, 17 Apr 2015 16:05:52 +0200 Subject: [PATCH 03/10] Correction so all aes encryption alg. can be used while decrypting. --- src/saml2/aes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/saml2/aes.py b/src/saml2/aes.py index 27a61aa..4110e1f 100644 --- a/src/saml2/aes.py +++ b/src/saml2/aes.py @@ -87,7 +87,7 @@ class AESCipher(object): return cmsg - def decrypt(self, msg, iv=None, padding="PKCS#7", b64dec=True): + def decrypt(self, msg, iv=None, alg="aes_128_cbc", padding="PKCS#7", b64dec=True): """ :param key: The encryption key :param iv: init vector @@ -102,7 +102,7 @@ class AESCipher(object): _iv = data[:AES.block_size] if iv: assert iv == _iv - cipher, iv = self.build_cipher(iv) + cipher, iv = self.build_cipher(iv, alg=alg) res = cipher.decrypt(data)[AES.block_size:] if padding in ["PKCS#5", "PKCS#7"]: res = res[:-ord(res[-1])] From f3a5df6e8f394109f4f804a5a6c3788bd417e5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Ho=CC=88rberg?= Date: Fri, 17 Apr 2015 16:07:39 +0200 Subject: [PATCH 04/10] Fix for encryption of assertions. --- src/saml2/sigver.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index 5a8fae6..efe96fe 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -1820,10 +1820,11 @@ def pre_encrypt_assertion(response): assertion = response.assertion response.assertion = None response.encrypted_assertion = EncryptedAssertion() - if isinstance(assertion, list): - response.encrypted_assertion.add_extension_elements(assertion) - else: - response.encrypted_assertion.add_extension_element(assertion) + if assertion is not None: + if isinstance(assertion, list): + response.encrypted_assertion.add_extension_elements(assertion) + else: + response.encrypted_assertion.add_extension_element(assertion) # txt = "%s" % response # _ass = "%s" % assertion # _ass = rm_xmltag(_ass) From 453061ca457ae5590a3d77bda70b2d9859f50abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Ho=CC=88rberg?= Date: Fri, 17 Apr 2015 16:08:34 +0200 Subject: [PATCH 05/10] Fix so the IdP follows PEFIM. --- example/idp2/idp.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/example/idp2/idp.py b/example/idp2/idp.py index 5d2a46e..8c42d36 100755 --- a/example/idp2/idp.py +++ b/example/idp2/idp.py @@ -335,6 +335,8 @@ class SSO(Service): _resp = IDP.create_authn_response( identity, userid=self.user, encrypt_cert=encrypt_cert, + encrypt_assertion_self_contained=True, + encrypted_advice_attributes=True, **resp_args) except Exception as excp: logging.error(exception_trace(excp)) @@ -399,9 +401,9 @@ class SSO(Service): return resp(self.environ, self.start_response) if self.user: + saml_msg["req_info"] = self.req_info if _req.force_authn is not None and \ _req.force_authn.lower() == 'true': - saml_msg["req_info"] = self.req_info key = self._store_request(saml_msg) return self.not_authn(key, _req.requested_authn_context) else: @@ -1013,6 +1015,7 @@ def application(environ, start_response): if isinstance(callback, tuple): cls = callback[0](environ, start_response, user) func = getattr(cls, callback[1]) + return func() return callback(environ, start_response, user) From eeacd0585875598e15356aefde65f8c7b635e25b Mon Sep 17 00:00:00 2001 From: Rebecka Gulliksson Date: Wed, 22 Apr 2015 07:20:52 +0200 Subject: [PATCH 06/10] Added path for alabaster theme in doc config. --- doc/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/conf.py b/doc/conf.py index 3e43897..9d26103 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -12,6 +12,7 @@ # serve to show the default. import sys, os +import alabaster # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -22,6 +23,7 @@ import sys, os # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. + extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage'] # Add any paths that contain templates here, relative to this directory. @@ -91,6 +93,7 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. +html_theme_path = [alabaster.get_path()] html_theme = 'alabaster' on_rtd = os.environ.get('READTHEDOCS', None) == 'True' From 5035a58137f7ba5ad79e4e52703b789b2a726955 Mon Sep 17 00:00:00 2001 From: Rebecka Gulliksson Date: Wed, 22 Apr 2015 09:30:25 +0200 Subject: [PATCH 07/10] Updated mdbcache to use the new MongoClient class and specific version of pymongo in setup.py. Connection class has been deprecated and was removed in pymongo 3.0. --- setup.py | 2 +- src/saml2/mdbcache.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 616779e..fce3d72 100755 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ install_requires = [ tests_require = [ 'mongodict', 'pyasn1', - 'pymongo', + 'pymongo==3.0.1', 'python-memcached == 1.51', 'pytest', 'mako', diff --git a/src/saml2/mdbcache.py b/src/saml2/mdbcache.py index 86ebd17..0817827 100644 --- a/src/saml2/mdbcache.py +++ b/src/saml2/mdbcache.py @@ -1,9 +1,9 @@ #!/usr/bin/env python import logging +from pymongo.mongo_client import MongoClient __author__ = 'rolandh' -from pymongo import Connection #import cjson import time from datetime import datetime @@ -18,9 +18,9 @@ logger = logging.getLogger(__name__) class Cache(object): def __init__(self, server=None, debug=0, db=None): if server: - connection = Connection(server) + connection = MongoClient(server) else: - connection = Connection() + connection = MongoClient() if db: self._db = connection[db] From d614026c3b84d376e0cd346448f3eda760b5e4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Ho=CC=88rberg?= Date: Wed, 22 Apr 2015 13:44:26 +0200 Subject: [PATCH 08/10] Fix so the wsgi SP follows PEFIM. --- example/sp-wsgi/pki/localhost.ca.crt | 15 +++++++++++++++ example/sp-wsgi/pki/localhost.ca.key | 15 +++++++++++++++ example/sp-wsgi/sp.py | 28 +++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 example/sp-wsgi/pki/localhost.ca.crt create mode 100644 example/sp-wsgi/pki/localhost.ca.key diff --git a/example/sp-wsgi/pki/localhost.ca.crt b/example/sp-wsgi/pki/localhost.ca.crt new file mode 100644 index 0000000..dd8d229 --- /dev/null +++ b/example/sp-wsgi/pki/localhost.ca.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSTCCAbICAQEwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCc2UxCzAJBgNV +BAgTAmFjMQ0wCwYDVQQHEwR1bWVhMRwwGgYDVQQKExNJVFMgVW1lYSBVbml2ZXJz +aXR5MQ0wCwYDVQQLEwRESVJHMRUwEwYDVQQDEwxsb2NhbGhvc3QuY2EwHhcNMTQw +MzE3MTY0MjM5WhcNMjQwMzE0MTY0MjM5WjBtMQswCQYDVQQGEwJzZTELMAkGA1UE +CBMCYWMxDTALBgNVBAcTBHVtZWExHDAaBgNVBAoTE0lUUyBVbWVhIFVuaXZlcnNp +dHkxDTALBgNVBAsTBERJUkcxFTATBgNVBAMTDGxvY2FsaG9zdC5jYTCBnzANBgkq +hkiG9w0BAQEFAAOBjQAwgYkCgYEA0+e/ncbbFMpsYc7Pb5wub2Q0jBpaaae3ZklR +8QNLgQnja6kkKseRR3oOBioo9e7qZbN1N6E2mIye/pMtlOuBcAmp3A+F4rn5VC5p +U9MMliLaYYY9369lrMk/1u/mOvHmrkV5XdkinR9cY82A2swexWrpNg0IXJorrWXM +l6mgCncCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBQmC3rYQCyB3iCJBhRF4H10EMw +NCNMPUiOMvu0+TOIz09hx71LjRbtxUQ+NeksuHJ3ii6UG2XIgXvFCaE0v/pB1UkK +oTqyfzUSZGvPyODEBX+erq5sQeUeONGHDIEK9c200kwlSnfHSoDSk/C3ceIYY/Ry +nD3fl05aOEowsVesMw== +-----END CERTIFICATE----- diff --git a/example/sp-wsgi/pki/localhost.ca.key b/example/sp-wsgi/pki/localhost.ca.key new file mode 100644 index 0000000..8ff65b3 --- /dev/null +++ b/example/sp-wsgi/pki/localhost.ca.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDT57+dxtsUymxhzs9vnC5vZDSMGlppp7dmSVHxA0uBCeNrqSQq +x5FHeg4GKij17upls3U3oTaYjJ7+ky2U64FwCancD4XiuflULmlT0wyWItphhj3f +r2WsyT/W7+Y68eauRXld2SKdH1xjzYDazB7Fauk2DQhcmiutZcyXqaAKdwIDAQAB +AoGBALORf19EVAKaRp3bkw0RXIgoch8HdfZymYekOjvyftkqd/2Tp4JY+1OGqruu +nmdJvnfQS1SI2KsM/u7b6ZR4vYWYqKIMRK5FTBjW0DNp70DV7Y7Y3Bl17xYfo2eJ +Zn80OOHBXyLrhWAnQCDERasXtFpHN5hYFoAx2S5YvyYef1aBAkEA8ZgBooYWkzAO +feEgIywvAaUtCv7TnLwZkTk5wUz4JavkX49LHjMdVef8vRohVOzv5+YPL8L/Aa+C +afsB56jtbQJBAOCKigLo9XtCrgm7j69r+C4MZaVPMEbXPzgOQFcsI5/K4FiGHeRF +9XCnVGJP7/tdRBbhX+CrQWIVy+fqqqQR3PMCQQDlQlky0Em17SGjZLIpjnEg/4zJ +5V4MWxdsD0D2ZUMKBJ9X61PeWaUZ82aMULKWs61Jg7Cfo0x4XgPE7HQ3UL/VAkAm +Ttx+5JnE/rpMhMhdyFDeIlVRH7J/04dAnAXUGX62a1ldIPyGjnzyTEn0P+kUfHP6 +Z1cBMJaGEmvoVDvqo/WLAkEAhIpFpf1j0c+FrtkGCmNPiANlpWJVz0u1Qt34adZS +R4GwY5YPKtQ9wo+Z0/K0sEuUBUQYv5nKlXCxRNcL1azr7g== +-----END RSA PRIVATE KEY----- diff --git a/example/sp-wsgi/sp.py b/example/sp-wsgi/sp.py index b67917b..1b72143 100755 --- a/example/sp-wsgi/sp.py +++ b/example/sp-wsgi/sp.py @@ -2,13 +2,14 @@ import logging import re import argparse +from saml2.extension.pefim import SPCertEnc import service_conf from Cookie import SimpleCookie from urlparse import parse_qs import sys -from saml2 import BINDING_HTTP_REDIRECT +from saml2 import BINDING_HTTP_REDIRECT, element_to_extension_element from saml2 import BINDING_SOAP from saml2 import time_util from saml2 import ecp @@ -33,6 +34,8 @@ from saml2.s_utils import UnsupportedBinding from saml2.s_utils import sid from saml2.s_utils import rndstr #from srtest import exception_trace +from saml2.md import Extensions +import xmldsig as ds logger = logging.getLogger("") hdlr = logging.FileHandler('spx.log') @@ -152,6 +155,7 @@ class Cache(object): self.uid2user = {} self.cookie_name = "spauthn" self.outstanding_queries = {} + self.outstanding_certs = {} self.relay_state = {} self.user = {} self.result = {} @@ -348,7 +352,7 @@ class ACS(Service): try: self.response = self.sp.parse_authn_request_response( - response, binding, self.outstanding_queries) + response, binding, self.outstanding_queries, self.cache.outstanding_certs) except UnknownPrincipal, excp: logger.error("UnknownPrincipal: %s" % (excp,)) resp = ServiceError("UnknownPrincipal: %s" % (excp,)) @@ -551,13 +555,31 @@ class SSO(object): "assertion_consumer_service"] # just pick one endp, return_binding = acs[0] + + extensions = None + cert = None + if _cli.config.generate_cert_func is not None: + cert_str, req_key_str = _cli.config.generate_cert_func() + cert = { + "cert": cert_str, + "key": req_key_str + } + spcertenc = SPCertEnc(x509_data=ds.X509Data( + x509_certificate=ds.X509Certificate(text=cert_str))) + extensions = Extensions(extension_elements=[ + element_to_extension_element(spcertenc)]) + req_id, req = _cli.create_authn_request(destination, - binding=return_binding) + binding=return_binding, extensions=extensions) _rstate = rndstr() self.cache.relay_state[_rstate] = came_from ht_args = _cli.apply_binding(_binding, "%s" % req, destination, relay_state=_rstate) _sid = req_id + + if cert is not None: + self.cache.outstanding_certs[_sid] = cert + except Exception, exc: logger.exception(exc) resp = ServiceError( From 00a3a04ade78d40f5c3245eec52d88bbe8525547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Ho=CC=88rberg?= Date: Wed, 22 Apr 2015 13:45:14 +0200 Subject: [PATCH 09/10] Removed files. --- example/sp-wsgi/pki/localhost.ca.crt | 15 --------------- example/sp-wsgi/pki/localhost.ca.key | 15 --------------- 2 files changed, 30 deletions(-) delete mode 100644 example/sp-wsgi/pki/localhost.ca.crt delete mode 100644 example/sp-wsgi/pki/localhost.ca.key diff --git a/example/sp-wsgi/pki/localhost.ca.crt b/example/sp-wsgi/pki/localhost.ca.crt deleted file mode 100644 index dd8d229..0000000 --- a/example/sp-wsgi/pki/localhost.ca.crt +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICSTCCAbICAQEwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCc2UxCzAJBgNV -BAgTAmFjMQ0wCwYDVQQHEwR1bWVhMRwwGgYDVQQKExNJVFMgVW1lYSBVbml2ZXJz -aXR5MQ0wCwYDVQQLEwRESVJHMRUwEwYDVQQDEwxsb2NhbGhvc3QuY2EwHhcNMTQw -MzE3MTY0MjM5WhcNMjQwMzE0MTY0MjM5WjBtMQswCQYDVQQGEwJzZTELMAkGA1UE -CBMCYWMxDTALBgNVBAcTBHVtZWExHDAaBgNVBAoTE0lUUyBVbWVhIFVuaXZlcnNp -dHkxDTALBgNVBAsTBERJUkcxFTATBgNVBAMTDGxvY2FsaG9zdC5jYTCBnzANBgkq -hkiG9w0BAQEFAAOBjQAwgYkCgYEA0+e/ncbbFMpsYc7Pb5wub2Q0jBpaaae3ZklR -8QNLgQnja6kkKseRR3oOBioo9e7qZbN1N6E2mIye/pMtlOuBcAmp3A+F4rn5VC5p -U9MMliLaYYY9369lrMk/1u/mOvHmrkV5XdkinR9cY82A2swexWrpNg0IXJorrWXM -l6mgCncCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBQmC3rYQCyB3iCJBhRF4H10EMw -NCNMPUiOMvu0+TOIz09hx71LjRbtxUQ+NeksuHJ3ii6UG2XIgXvFCaE0v/pB1UkK -oTqyfzUSZGvPyODEBX+erq5sQeUeONGHDIEK9c200kwlSnfHSoDSk/C3ceIYY/Ry -nD3fl05aOEowsVesMw== ------END CERTIFICATE----- diff --git a/example/sp-wsgi/pki/localhost.ca.key b/example/sp-wsgi/pki/localhost.ca.key deleted file mode 100644 index 8ff65b3..0000000 --- a/example/sp-wsgi/pki/localhost.ca.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQDT57+dxtsUymxhzs9vnC5vZDSMGlppp7dmSVHxA0uBCeNrqSQq -x5FHeg4GKij17upls3U3oTaYjJ7+ky2U64FwCancD4XiuflULmlT0wyWItphhj3f -r2WsyT/W7+Y68eauRXld2SKdH1xjzYDazB7Fauk2DQhcmiutZcyXqaAKdwIDAQAB -AoGBALORf19EVAKaRp3bkw0RXIgoch8HdfZymYekOjvyftkqd/2Tp4JY+1OGqruu -nmdJvnfQS1SI2KsM/u7b6ZR4vYWYqKIMRK5FTBjW0DNp70DV7Y7Y3Bl17xYfo2eJ -Zn80OOHBXyLrhWAnQCDERasXtFpHN5hYFoAx2S5YvyYef1aBAkEA8ZgBooYWkzAO -feEgIywvAaUtCv7TnLwZkTk5wUz4JavkX49LHjMdVef8vRohVOzv5+YPL8L/Aa+C -afsB56jtbQJBAOCKigLo9XtCrgm7j69r+C4MZaVPMEbXPzgOQFcsI5/K4FiGHeRF -9XCnVGJP7/tdRBbhX+CrQWIVy+fqqqQR3PMCQQDlQlky0Em17SGjZLIpjnEg/4zJ -5V4MWxdsD0D2ZUMKBJ9X61PeWaUZ82aMULKWs61Jg7Cfo0x4XgPE7HQ3UL/VAkAm -Ttx+5JnE/rpMhMhdyFDeIlVRH7J/04dAnAXUGX62a1ldIPyGjnzyTEn0P+kUfHP6 -Z1cBMJaGEmvoVDvqo/WLAkEAhIpFpf1j0c+FrtkGCmNPiANlpWJVz0u1Qt34adZS -R4GwY5YPKtQ9wo+Z0/K0sEuUBUQYv5nKlXCxRNcL1azr7g== ------END RSA PRIVATE KEY----- From ef30025b638e4957066b614c9c68ededb841dd42 Mon Sep 17 00:00:00 2001 From: Benjamin Fernandes Date: Wed, 22 Apr 2015 10:36:32 -0400 Subject: [PATCH 10/10] Require pycrypto 2.5+ It is required to use PKCS#1 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 616779e..d4cc7ea 100755 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ install_requires = [ 'paste', 'zope.interface', 'repoze.who', - 'pycrypto >= 2.2', # 'Crypto' + 'pycrypto >= 2.5', # 'Crypto' 'pytz', 'pyOpenSSL', 'python-dateutil',