summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland Hedberg <roland.hedberg@adm.umu.se>2014-12-15 11:49:23 +0100
committerRoland Hedberg <roland.hedberg@adm.umu.se>2014-12-15 11:49:23 +0100
commit845272f97d0a8cd8cab6224803434bce6c69537e (patch)
tree265ad5e233e0d7fdf519f5d3c65963bbe6ecd317
parenta0b34d0ebd76dac7a7b6f35804306ad1faceb59d (diff)
downloadpysaml2-845272f97d0a8cd8cab6224803434bce6c69537e.tar.gz
Fixed making redirect signature
-rw-r--r--src/saml2/entity.py10
-rw-r--r--src/saml2/httpbase.py49
-rw-r--r--src/saml2/mdstore.py4
-rw-r--r--src/saml2/pack.py7
-rw-r--r--src/saml2/request.py11
-rw-r--r--src/saml2/sigver.py15
-rw-r--r--tests/test_51_client.py22
7 files changed, 76 insertions, 42 deletions
diff --git a/src/saml2/entity.py b/src/saml2/entity.py
index 07d6415d..3082b323 100644
--- a/src/saml2/entity.py
+++ b/src/saml2/entity.py
@@ -2,6 +2,7 @@ import base64
from binascii import hexlify
import logging
from hashlib import sha1
+from Crypto.PublicKey import RSA
import requests
from saml2.metadata import ENDPOINTS
from saml2.profile import paos, ecp
@@ -133,6 +134,12 @@ class Entity(HTTPBase):
raise Exception(
"Could not fetch certificate from %s" % _val)
+ try:
+ self.signkey = RSA.importKey(
+ open(self.config.getattr("key_file", ""), 'r').read())
+ except KeyError:
+ self.signkey = None
+
HTTPBase.__init__(self, self.config.verify_ssl_cert,
self.config.ca_certs, self.config.key_file,
self.config.cert_file)
@@ -201,7 +208,8 @@ class Entity(HTTPBase):
info["method"] = "GET"
elif binding == BINDING_HTTP_REDIRECT:
logger.info("HTTP REDIRECT")
- info = self.use_http_get(msg_str, destination, relay_state, typ)
+ info = self.use_http_get(msg_str, destination, relay_state, typ,
+ **kwargs)
info["url"] = str(destination)
info["method"] = "GET"
elif binding == BINDING_SOAP or binding == BINDING_PAOS:
diff --git a/src/saml2/httpbase.py b/src/saml2/httpbase.py
index e65ed845..618bcfd9 100644
--- a/src/saml2/httpbase.py
+++ b/src/saml2/httpbase.py
@@ -247,7 +247,8 @@ class HTTPBase(object):
return r
- def use_http_form_post(self, message, destination, relay_state,
+ @staticmethod
+ def use_http_form_post(message, destination, relay_state,
typ="SAMLRequest"):
"""
Return a form that will automagically execute and POST the message
@@ -264,24 +265,8 @@ class HTTPBase(object):
return http_form_post_message(message, destination, relay_state, typ)
- def use_http_get(self, message, destination, relay_state,
- typ="SAMLRequest"):
- """
- Send a message using GET, this is the HTTP-Redirect case so
- no direct response is expected to this request.
-
- :param message:
- :param destination:
- :param relay_state:
- :param typ: Whether a Request, Response or Artifact
- :return: dictionary
- """
- if not isinstance(message, basestring):
- message = "%s" % (message,)
-
- return http_redirect_message(message, destination, relay_state, typ)
-
- def use_http_artifact(self, message, destination="", relay_state=""):
+ @staticmethod
+ def use_http_artifact(message, destination="", relay_state=""):
if relay_state:
query = urllib.urlencode({"SAMLart": message,
"RelayState": relay_state})
@@ -293,7 +278,8 @@ class HTTPBase(object):
}
return info
- def use_http_uri(self, message, typ, destination="", relay_state=""):
+ @staticmethod
+ def use_http_uri(message, typ, destination="", relay_state=""):
if typ == "SAMLResponse":
info = {
"data": message.split("\n")[1],
@@ -355,7 +341,7 @@ class HTTPBase(object):
:return:
"""
- #_response = self.server.post(soap_message, headers, path=path)
+ # _response = self.server.post(soap_message, headers, path=path)
try:
args = self.use_soap(request, destination, headers, sign)
args["headers"] = dict(args["headers"])
@@ -373,3 +359,24 @@ class HTTPBase(object):
def add_credentials(self, user, passwd):
self.user = user
self.passwd = passwd
+
+ @staticmethod
+ def use_http_get(message, destination, relay_state,
+ typ="SAMLRequest", sigalg="", key=None, **kwargs):
+ """
+ Send a message using GET, this is the HTTP-Redirect case so
+ no direct response is expected to this request.
+
+ :param message:
+ :param destination:
+ :param relay_state:
+ :param typ: Whether a Request, Response or Artifact
+ :param sigalg: The signature algorithm to use.
+ :param key: Key to use for signing
+ :return: dictionary
+ """
+ if not isinstance(message, basestring):
+ message = "%s" % (message,)
+
+ return http_redirect_message(message, destination, relay_state, typ,
+ sigalg, key)
diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py
index e2c6abe5..4586c448 100644
--- a/src/saml2/mdstore.py
+++ b/src/saml2/mdstore.py
@@ -5,7 +5,6 @@ import sys
import json
from hashlib import sha1
-from urllib import urlencode, quote_plus
from os.path import isfile, join
from saml2.httpbase import HTTPBase
from saml2.extension.idpdisc import BINDING_DISCO
@@ -20,7 +19,8 @@ from saml2 import SAMLError
from saml2 import BINDING_HTTP_REDIRECT
from saml2 import BINDING_HTTP_POST
from saml2 import BINDING_SOAP
-from saml2.s_utils import UnsupportedBinding, UnknownSystemEntity
+from saml2.s_utils import UnsupportedBinding
+from saml2.s_utils import UnknownSystemEntity
from saml2.sigver import split_len
from saml2.validate import valid_instance
from saml2.time_util import valid
diff --git a/src/saml2/pack.py b/src/saml2/pack.py
index 9a10cbe9..40481cd3 100644
--- a/src/saml2/pack.py
+++ b/src/saml2/pack.py
@@ -113,9 +113,7 @@ def http_redirect_message(message, location, relay_state="", typ="SAMLRequest",
args["RelayState"] = relay_state
if sigalg:
- # sigalgs
- # http://www.w3.org/2000/09/xmldsig#dsa-sha1
- # http://www.w3.org/2000/09/xmldsig#rsa-sha1
+ # sigalgs, one of the ones defined in xmldsig
args["SigAlg"] = sigalg
@@ -124,7 +122,8 @@ def http_redirect_message(message, location, relay_state="", typ="SAMLRequest",
except:
raise Unsupported("Signing algorithm")
else:
- string = "&".join([urllib.urlencode({k: args[k]}) for k in _order if k in args])
+ string = "&".join([urllib.urlencode({k: args[k]})
+ for k in _order if k in args])
args["Signature"] = base64.b64encode(signer.sign(string, key))
string = urllib.urlencode(args)
else:
diff --git a/src/saml2/request.py b/src/saml2/request.py
index 17f3edf3..602a56f7 100644
--- a/src/saml2/request.py
+++ b/src/saml2/request.py
@@ -11,8 +11,8 @@ from saml2.response import IncorrectlySigned
logger = logging.getLogger(__name__)
-def _dummy(_arg):
- return None
+def _dummy(data, **_arg):
+ return ""
class Request(object):
@@ -36,12 +36,15 @@ class Request(object):
self.message = None
self.not_on_or_after = 0
- def _loads(self, xmldata, binding=None, origdoc=None, must=None, only_valid_cert=False):
+ def _loads(self, xmldata, binding=None, origdoc=None, must=None,
+ only_valid_cert=False):
# own copy
self.xmlstr = xmldata[:]
logger.info("xmlstr: %s" % (self.xmlstr,))
try:
- self.message = self.signature_check(xmldata, origdoc=origdoc, must=must, only_valid_cert=only_valid_cert)
+ self.message = self.signature_check(xmldata, origdoc=origdoc,
+ must=must,
+ only_valid_cert=only_valid_cert)
except TypeError:
raise
except Exception, excp:
diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py
index 0847b671..e15c64e1 100644
--- a/src/saml2/sigver.py
+++ b/src/saml2/sigver.py
@@ -43,8 +43,6 @@ from saml2.time_util import str_to_time
from tempfile import NamedTemporaryFile
from subprocess import Popen, PIPE
-from xmldsig import digest_default
-from xmldsig import sig_default
from xmldsig import SIG_RSA_SHA1
from xmldsig import SIG_RSA_SHA224
from xmldsig import SIG_RSA_SHA256
@@ -81,10 +79,6 @@ class CertificateTooOld(SigverError):
pass
-class SignatureError(SigverError):
- pass
-
-
class XmlsecError(SigverError):
pass
@@ -101,6 +95,10 @@ class EncryptError(XmlsecError):
pass
+class SignatureError(XmlsecError):
+ pass
+
+
class BadSignature(SigverError):
"""The signature is invalid."""
pass
@@ -912,7 +910,8 @@ class CryptoBackendXmlSec1(CryptoBackend):
:param exception: The exception class to raise on errors
:result: Whatever xmlsec wrote to an --output temporary file
"""
- ntf = NamedTemporaryFile(suffix=".xml", delete=self._xmlsec_delete_tmpfiles)
+ ntf = NamedTemporaryFile(suffix=".xml",
+ delete=self._xmlsec_delete_tmpfiles)
com_list.extend(["--output", ntf.name])
com_list += extra_args
@@ -1152,7 +1151,7 @@ class CertHandler(object):
self._cert_info = None
self._generate_cert_func_active = False
if generate_cert_info is not None and len(self._cert_str) > 0 and \
- len(self._key_str) > 0 and tmp_key_file is not \
+ len(self._key_str) > 0 and tmp_key_file is not \
None and tmp_cert_file is not None:
self._generate_cert = True
self._cert_info = generate_cert_info
diff --git a/tests/test_51_client.py b/tests/test_51_client.py
index 762832b2..8baf3103 100644
--- a/tests/test_51_client.py
+++ b/tests/test_51_client.py
@@ -4,6 +4,8 @@
import base64
import urllib
import urlparse
+from Crypto.PublicKey import RSA
+from xmldsig import SIG_RSA_SHA256
from saml2 import BINDING_HTTP_POST
from saml2 import BINDING_HTTP_REDIRECT
from saml2 import config
@@ -492,6 +494,22 @@ class TestClient:
assert resp.assertion
assert resp.ava == {'givenName': ['Derek'], 'sn': ['Jeter']}
+ def test_signed_redirect(self):
+
+ msg_str = "%s" % self.client.create_authn_request(
+ "http://www.example.com/sso", message_id="id1")[1]
+
+ key = self.client.signkey
+
+ info = self.client.apply_binding(
+ BINDING_HTTP_REDIRECT, msg_str, destination="",
+ relay_state="relay2", sigalg=SIG_RSA_SHA256, key=key)
+
+ loc = info["headers"][0][1]
+ qs = urlparse.parse_qs(loc[1:])
+ assert _leq(qs.keys(),
+ ['SigAlg', 'SAMLRequest', 'RelayState', 'Signature'])
+
# Below can only be done with dummy Server
IDP = "urn:mace:example.com:saml:roland:idp"
@@ -596,6 +614,6 @@ class TestClientWithDummy():
# tc.test_response()
if __name__ == "__main__":
- tc = TestClientWithDummy()
+ tc = TestClient()
tc.setup_class()
- tc.test_do_attribute_query()
+ tc.test_signed_redirect()