Skip to content

Commit e43f63c

Browse files
author
Hans Hörberg
committed
Fixes for EncryptedAssertion and signing.
1 parent 495d5f6 commit e43f63c

File tree

9 files changed

+47
-25
lines changed

9 files changed

+47
-25
lines changed

example/idp2/idp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ def do(self, query, binding_in, relay_state="", encrypt_cert=None):
301301
try:
302302
_resp = IDP.create_authn_response(
303303
identity, userid=self.user,
304-
authn=AUTHN_BROKER[self.environ["idp.authn_ref"]], sign_response=False, encrypt_cert=encrypt_cert,
304+
authn=AUTHN_BROKER[self.environ["idp.authn_ref"]], encrypt_cert=encrypt_cert,
305305
**resp_args)
306306
except Exception, excp:
307307
logging.error(exception_trace(excp))

example/sp-repoze/sp_conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def generate_cert():
7878
#Information needed for generated cert (NO CERT) solution.
7979
"authn_requests_signed": "true", #Will sign the request!
8080
"want_assertions_signed": "false", #Demands that the assertion is signed.
81+
"want_response_signed": "true",
8182
"allow_unsolicited": "true", #Allows the message not to be ment for this sp.
8283
#############################################################
8384
"name": "LocalTestSPHans",

src/saml2/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ def to_string(self, nspair=None):
558558
except AttributeError:
559559
# Backwards compatibility with ET < 1.3
560560
ElementTree._namespace_map[uri] = prefix
561+
except ValueError:
562+
pass
561563

562564
return ElementTree.tostring(self._to_element_tree(), encoding="UTF-8")
563565

src/saml2/client_base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,9 @@ def __init__(self, config=None, identity_cache=None, state_cache=None,
122122
self.allow_unsolicited = False
123123
self.authn_requests_signed = False
124124
self.want_assertions_signed = False
125+
self.want_response_signed = False
125126
for foo in ["allow_unsolicited", "authn_requests_signed",
126-
"logout_requests_signed", "want_assertions_signed"]:
127+
"logout_requests_signed", "want_assertions_signed", "want_response_signed"]:
127128
v = self.config.getattr(foo, "sp")
128129
if v is True or v == 'true':
129130
setattr(self, foo, True)
@@ -530,6 +531,7 @@ def parse_authn_request_response(self, xmlstr, binding, outstanding=None, outsta
530531
"outstanding_certs": outstanding_certs,
531532
"allow_unsolicited": self.allow_unsolicited,
532533
"want_assertions_signed": self.want_assertions_signed,
534+
"want_response_signed": self.want_response_signed,
533535
"return_addrs": self.service_urls(),
534536
"entity_id": self.config.entityid,
535537
"attribute_converters": self.config.attribute_converters,

src/saml2/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"idp",
8181
"aa",
8282
"subject_data",
83+
"want_response_signed",
8384
"want_assertions_signed",
8485
"authn_requests_signed",
8586
"name_form",

src/saml2/entity.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,16 @@ def _response(self, in_response_to, consumer_url=None, status=None,
460460
return signed_instance_factory(response, self.sec, to_sign)
461461

462462
if encrypt_assertion:
463+
sign_class = [(class_name(response), response.id)]
464+
if sign:
465+
response.signature = pre_signature_part(response.id, self.sec.my_cert, 1)
463466
cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
464467
_, cert_file = make_temp("%s" % encrypt_cert, decode=False)
465-
return cbxs.encrypt_assertion(response, cert_file, pre_encryption_part())#template(response.assertion.id))
466-
#response = response_from_string(response_str)
468+
response = cbxs.encrypt_assertion(response, cert_file, pre_encryption_part())#template(response.assertion.id))
469+
if sign:
470+
return signed_instance_factory(response, self.sec, sign_class)
471+
else:
472+
return response
467473

468474
if sign:
469475
return self.sign(response, to_sign=to_sign)
@@ -811,16 +817,18 @@ def _parse_response(self, xmlstr, response_cls, service, binding, outstanding_ce
811817
raise
812818

813819
xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
820+
origxml = xmlstr
814821
if outstanding_certs is not None:
815822
_response = samlp.any_response_from_string(xmlstr)
816-
_, cert_file = make_temp("%s" % outstanding_certs[_response.in_response_to]["key"], decode=False)
817-
cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
818-
xmlstr = cbxs.decrypt(xmlstr, cert_file)
823+
if len(_response.encrypted_assertion) > 0:
824+
_, cert_file = make_temp("%s" % outstanding_certs[_response.in_response_to]["key"], decode=False)
825+
cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
826+
xmlstr = cbxs.decrypt(xmlstr, cert_file)
819827
if not xmlstr: # Not a valid reponse
820828
return None
821829

822830
try:
823-
response = response.loads(xmlstr, False)
831+
response = response.loads(xmlstr, False, origxml=origxml)
824832
except SigverError, err:
825833
logger.error("Signature Error: %s" % err)
826834
return None
@@ -831,6 +839,13 @@ def _parse_response(self, xmlstr, response_cls, service, binding, outstanding_ce
831839

832840
logger.debug("XMLSTR: %s" % xmlstr)
833841

842+
for encrypted_assertion in response.response.encrypted_assertion:
843+
if encrypted_assertion.extension_elements is not None:
844+
assertion_list = extension_elements_to_elements(encrypted_assertion.extension_elements, [saml])
845+
for assertion in assertion_list:
846+
_assertion = saml.assertion_from_string(str(assertion))
847+
response.response.assertion.append(_assertion)
848+
834849
if response:
835850
response = response.verify()
836851

src/saml2/response.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ def __init__(self, sec_context, return_addrs=None, timeslack=0,
268268
self.in_response_to = None
269269
self.signature_check = self.sec.correctly_signed_response
270270
self.require_signature = False
271+
self.require_response_signature = False
271272
self.not_signed = False
272273
self.asynchop = asynchop
273274

@@ -318,7 +319,9 @@ def _loads(self, xmldata, decode=True, origxml=None):
318319
logger.debug("xmlstr: %s" % (self.xmlstr,))
319320

320321
try:
321-
self.response = self.signature_check(xmldata, origdoc=origxml, must=self.require_signature)
322+
self.response = self.signature_check(xmldata, origdoc=origxml, must=self.require_signature,
323+
require_response_signature=self.require_response_signature)
324+
322325
except TypeError:
323326
raise
324327
except SignatureError:
@@ -452,7 +455,7 @@ def __init__(self, sec_context, attribute_converters, entity_id,
452455
return_addrs=None, outstanding_queries=None,
453456
timeslack=0, asynchop=True, allow_unsolicited=False,
454457
test=False, allow_unknown_attributes=False,
455-
want_assertions_signed=False, **kwargs):
458+
want_assertions_signed=False, want_response_signed=False, **kwargs):
456459

457460
StatusResponse.__init__(self, sec_context, return_addrs, timeslack,
458461
asynchop=asynchop)
@@ -469,6 +472,7 @@ def __init__(self, sec_context, attribute_converters, entity_id,
469472
self.session_not_on_or_after = 0
470473
self.allow_unsolicited = allow_unsolicited
471474
self.require_signature = want_assertions_signed
475+
self.require_response_signature = want_response_signed
472476
self.test = test
473477
self.allow_unknown_attributes = allow_unknown_attributes
474478
#

src/saml2/server.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,8 @@ def create_attribute_response(self, identity, in_response_to, destination,
427427
def create_authn_response(self, identity, in_response_to, destination,
428428
sp_entity_id, name_id_policy=None, userid=None,
429429
name_id=None, authn=None, issuer=None,
430-
sign_response=False, sign_assertion=None, encrypt_cert=None, **kwargs):
430+
sign_response=None, sign_assertion=None, encrypt_cert=None, encrypt_assertion=None,
431+
**kwargs):
431432
""" Constructs an AuthenticationResponse
432433
433434
:param identity: Information about an user
@@ -465,7 +466,11 @@ def create_authn_response(self, identity, in_response_to, destination,
465466
if sign_response is None:
466467
sign_response = False
467468

468-
encrypt_assertion = self.config.getattr("encrypt_assertion", "idp")
469+
if encrypt_assertion is None:
470+
encrypt_assertion = self.config.getattr("encrypt_assertion", "idp")
471+
if encrypt_assertion is None:
472+
encrypt_assertion = False
473+
469474
if encrypt_assertion:
470475
if encrypt_cert is not None:
471476
verify_encrypt_cert = self.config.getattr("verify_encrypt_cert", "idp")

src/saml2/sigver.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,7 +1474,7 @@ def correctly_signed_assertion_id_response(self, decoded_xml, must=False,
14741474
return self.correctly_signed_message(decoded_xml, "assertion", must,
14751475
origdoc, only_valid_cert)
14761476

1477-
def correctly_signed_response(self, decoded_xml, must=False, origdoc=None):
1477+
def correctly_signed_response(self, decoded_xml, must=False, origdoc=None, require_response_signature=False):
14781478
""" Check if a instance is correctly signed, if we have metadata for
14791479
the IdP that sent the info use that, if not use the key that are in
14801480
the message if any.
@@ -1492,26 +1492,18 @@ def correctly_signed_response(self, decoded_xml, must=False, origdoc=None):
14921492
if response.signature:
14931493
self._check_signature(decoded_xml, response, class_name(response),
14941494
origdoc)
1495+
elif require_response_signature:
1496+
raise SignatureError("Signature missing for response")
14951497

14961498
if isinstance(response, Response) and (response.assertion or
14971499
response.encrypted_assertion):
14981500
# Try to find the signing cert in the assertion
14991501
for assertion in (
15001502
response.assertion or response.encrypted_assertion):
1501-
if response.encrypted_assertion:
1502-
assertion_list = extension_elements_to_elements(assertion.extension_elements, [saml])
1503-
if len(assertion_list) > 0:
1504-
assertion = saml.assertion_from_string(str(assertion_list[0]))
1505-
response.assertion.append(assertion)
1506-
else:
1507-
decoded_xml = self.decrypt(assertion.encrypted_data.to_string())
1508-
assertion = saml.assertion_from_string(decoded_xml)
1509-
response.assertion.append(assertion)
1510-
1511-
if not assertion.signature:
1503+
if not hasattr(assertion, 'signature') or not assertion.signature:
15121504
logger.debug("unsigned")
15131505
if must:
1514-
raise SignatureError("Signature missing")
1506+
raise SignatureError("Signature missing for assertion")
15151507
continue
15161508
else:
15171509
logger.debug("signed")

0 commit comments

Comments
 (0)