Skip to content

Commit 845272f

Browse files
author
Roland Hedberg
committed
Fixed making redirect signature
1 parent a0b34d0 commit 845272f

File tree

7 files changed

+76
-42
lines changed

7 files changed

+76
-42
lines changed

src/saml2/entity.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from binascii import hexlify
33
import logging
44
from hashlib import sha1
5+
from Crypto.PublicKey import RSA
56
import requests
67
from saml2.metadata import ENDPOINTS
78
from saml2.profile import paos, ecp
@@ -133,6 +134,12 @@ def __init__(self, entity_type, config=None, config_file="",
133134
raise Exception(
134135
"Could not fetch certificate from %s" % _val)
135136

137+
try:
138+
self.signkey = RSA.importKey(
139+
open(self.config.getattr("key_file", ""), 'r').read())
140+
except KeyError:
141+
self.signkey = None
142+
136143
HTTPBase.__init__(self, self.config.verify_ssl_cert,
137144
self.config.ca_certs, self.config.key_file,
138145
self.config.cert_file)
@@ -201,7 +208,8 @@ def apply_binding(self, binding, msg_str, destination="", relay_state="",
201208
info["method"] = "GET"
202209
elif binding == BINDING_HTTP_REDIRECT:
203210
logger.info("HTTP REDIRECT")
204-
info = self.use_http_get(msg_str, destination, relay_state, typ)
211+
info = self.use_http_get(msg_str, destination, relay_state, typ,
212+
**kwargs)
205213
info["url"] = str(destination)
206214
info["method"] = "GET"
207215
elif binding == BINDING_SOAP or binding == BINDING_PAOS:

src/saml2/httpbase.py

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ def send(self, url, method="GET", **kwargs):
247247

248248
return r
249249

250-
def use_http_form_post(self, message, destination, relay_state,
250+
@staticmethod
251+
def use_http_form_post(message, destination, relay_state,
251252
typ="SAMLRequest"):
252253
"""
253254
Return a form that will automagically execute and POST the message
@@ -264,24 +265,8 @@ def use_http_form_post(self, message, destination, relay_state,
264265

265266
return http_form_post_message(message, destination, relay_state, typ)
266267

267-
def use_http_get(self, message, destination, relay_state,
268-
typ="SAMLRequest"):
269-
"""
270-
Send a message using GET, this is the HTTP-Redirect case so
271-
no direct response is expected to this request.
272-
273-
:param message:
274-
:param destination:
275-
:param relay_state:
276-
:param typ: Whether a Request, Response or Artifact
277-
:return: dictionary
278-
"""
279-
if not isinstance(message, basestring):
280-
message = "%s" % (message,)
281-
282-
return http_redirect_message(message, destination, relay_state, typ)
283-
284-
def use_http_artifact(self, message, destination="", relay_state=""):
268+
@staticmethod
269+
def use_http_artifact(message, destination="", relay_state=""):
285270
if relay_state:
286271
query = urllib.urlencode({"SAMLart": message,
287272
"RelayState": relay_state})
@@ -293,7 +278,8 @@ def use_http_artifact(self, message, destination="", relay_state=""):
293278
}
294279
return info
295280

296-
def use_http_uri(self, message, typ, destination="", relay_state=""):
281+
@staticmethod
282+
def use_http_uri(message, typ, destination="", relay_state=""):
297283
if typ == "SAMLResponse":
298284
info = {
299285
"data": message.split("\n")[1],
@@ -355,7 +341,7 @@ def send_using_soap(self, request, destination, headers=None, sign=False):
355341
:return:
356342
"""
357343

358-
#_response = self.server.post(soap_message, headers, path=path)
344+
# _response = self.server.post(soap_message, headers, path=path)
359345
try:
360346
args = self.use_soap(request, destination, headers, sign)
361347
args["headers"] = dict(args["headers"])
@@ -373,3 +359,24 @@ def send_using_soap(self, request, destination, headers=None, sign=False):
373359
def add_credentials(self, user, passwd):
374360
self.user = user
375361
self.passwd = passwd
362+
363+
@staticmethod
364+
def use_http_get(message, destination, relay_state,
365+
typ="SAMLRequest", sigalg="", key=None, **kwargs):
366+
"""
367+
Send a message using GET, this is the HTTP-Redirect case so
368+
no direct response is expected to this request.
369+
370+
:param message:
371+
:param destination:
372+
:param relay_state:
373+
:param typ: Whether a Request, Response or Artifact
374+
:param sigalg: The signature algorithm to use.
375+
:param key: Key to use for signing
376+
:return: dictionary
377+
"""
378+
if not isinstance(message, basestring):
379+
message = "%s" % (message,)
380+
381+
return http_redirect_message(message, destination, relay_state, typ,
382+
sigalg, key)

src/saml2/mdstore.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import json
66

77
from hashlib import sha1
8-
from urllib import urlencode, quote_plus
98
from os.path import isfile, join
109
from saml2.httpbase import HTTPBase
1110
from saml2.extension.idpdisc import BINDING_DISCO
@@ -20,7 +19,8 @@
2019
from saml2 import BINDING_HTTP_REDIRECT
2120
from saml2 import BINDING_HTTP_POST
2221
from saml2 import BINDING_SOAP
23-
from saml2.s_utils import UnsupportedBinding, UnknownSystemEntity
22+
from saml2.s_utils import UnsupportedBinding
23+
from saml2.s_utils import UnknownSystemEntity
2424
from saml2.sigver import split_len
2525
from saml2.validate import valid_instance
2626
from saml2.time_util import valid

src/saml2/pack.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,7 @@ def http_redirect_message(message, location, relay_state="", typ="SAMLRequest",
113113
args["RelayState"] = relay_state
114114

115115
if sigalg:
116-
# sigalgs
117-
# http://www.w3.org/2000/09/xmldsig#dsa-sha1
118-
# http://www.w3.org/2000/09/xmldsig#rsa-sha1
116+
# sigalgs, one of the ones defined in xmldsig
119117

120118
args["SigAlg"] = sigalg
121119

@@ -124,7 +122,8 @@ def http_redirect_message(message, location, relay_state="", typ="SAMLRequest",
124122
except:
125123
raise Unsupported("Signing algorithm")
126124
else:
127-
string = "&".join([urllib.urlencode({k: args[k]}) for k in _order if k in args])
125+
string = "&".join([urllib.urlencode({k: args[k]})
126+
for k in _order if k in args])
128127
args["Signature"] = base64.b64encode(signer.sign(string, key))
129128
string = urllib.urlencode(args)
130129
else:

src/saml2/request.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
logger = logging.getLogger(__name__)
1212

1313

14-
def _dummy(_arg):
15-
return None
14+
def _dummy(data, **_arg):
15+
return ""
1616

1717

1818
class Request(object):
@@ -36,12 +36,15 @@ def _clear(self):
3636
self.message = None
3737
self.not_on_or_after = 0
3838

39-
def _loads(self, xmldata, binding=None, origdoc=None, must=None, only_valid_cert=False):
39+
def _loads(self, xmldata, binding=None, origdoc=None, must=None,
40+
only_valid_cert=False):
4041
# own copy
4142
self.xmlstr = xmldata[:]
4243
logger.info("xmlstr: %s" % (self.xmlstr,))
4344
try:
44-
self.message = self.signature_check(xmldata, origdoc=origdoc, must=must, only_valid_cert=only_valid_cert)
45+
self.message = self.signature_check(xmldata, origdoc=origdoc,
46+
must=must,
47+
only_valid_cert=only_valid_cert)
4548
except TypeError:
4649
raise
4750
except Exception, excp:

src/saml2/sigver.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@
4343
from tempfile import NamedTemporaryFile
4444
from subprocess import Popen, PIPE
4545

46-
from xmldsig import digest_default
47-
from xmldsig import sig_default
4846
from xmldsig import SIG_RSA_SHA1
4947
from xmldsig import SIG_RSA_SHA224
5048
from xmldsig import SIG_RSA_SHA256
@@ -81,10 +79,6 @@ class CertificateTooOld(SigverError):
8179
pass
8280

8381

84-
class SignatureError(SigverError):
85-
pass
86-
87-
8882
class XmlsecError(SigverError):
8983
pass
9084

@@ -101,6 +95,10 @@ class EncryptError(XmlsecError):
10195
pass
10296

10397

98+
class SignatureError(XmlsecError):
99+
pass
100+
101+
104102
class BadSignature(SigverError):
105103
"""The signature is invalid."""
106104
pass
@@ -912,7 +910,8 @@ def _run_xmlsec(self, com_list, extra_args, validate_output=True,
912910
:param exception: The exception class to raise on errors
913911
:result: Whatever xmlsec wrote to an --output temporary file
914912
"""
915-
ntf = NamedTemporaryFile(suffix=".xml", delete=self._xmlsec_delete_tmpfiles)
913+
ntf = NamedTemporaryFile(suffix=".xml",
914+
delete=self._xmlsec_delete_tmpfiles)
916915
com_list.extend(["--output", ntf.name])
917916
com_list += extra_args
918917

@@ -1152,7 +1151,7 @@ def __init__(self, security_context, cert_file=None, cert_type="pem",
11521151
self._cert_info = None
11531152
self._generate_cert_func_active = False
11541153
if generate_cert_info is not None and len(self._cert_str) > 0 and \
1155-
len(self._key_str) > 0 and tmp_key_file is not \
1154+
len(self._key_str) > 0 and tmp_key_file is not \
11561155
None and tmp_cert_file is not None:
11571156
self._generate_cert = True
11581157
self._cert_info = generate_cert_info

tests/test_51_client.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import base64
55
import urllib
66
import urlparse
7+
from Crypto.PublicKey import RSA
8+
from xmldsig import SIG_RSA_SHA256
79
from saml2 import BINDING_HTTP_POST
810
from saml2 import BINDING_HTTP_REDIRECT
911
from saml2 import config
@@ -492,6 +494,22 @@ def test_sign_then_encrypt_assertion2(self):
492494
assert resp.assertion
493495
assert resp.ava == {'givenName': ['Derek'], 'sn': ['Jeter']}
494496

497+
def test_signed_redirect(self):
498+
499+
msg_str = "%s" % self.client.create_authn_request(
500+
"http://www.example.com/sso", message_id="id1")[1]
501+
502+
key = self.client.signkey
503+
504+
info = self.client.apply_binding(
505+
BINDING_HTTP_REDIRECT, msg_str, destination="",
506+
relay_state="relay2", sigalg=SIG_RSA_SHA256, key=key)
507+
508+
loc = info["headers"][0][1]
509+
qs = urlparse.parse_qs(loc[1:])
510+
assert _leq(qs.keys(),
511+
['SigAlg', 'SAMLRequest', 'RelayState', 'Signature'])
512+
495513
# Below can only be done with dummy Server
496514
IDP = "urn:mace:example.com:saml:roland:idp"
497515

@@ -596,6 +614,6 @@ def test_post_sso(self):
596614
# tc.test_response()
597615

598616
if __name__ == "__main__":
599-
tc = TestClientWithDummy()
617+
tc = TestClient()
600618
tc.setup_class()
601-
tc.test_do_attribute_query()
619+
tc.test_signed_redirect()

0 commit comments

Comments
 (0)