Skip to content

Commit 476f32f

Browse files
committed
Add DigestMethod support. Add sign_algorithm and digest_algorithm parameters to sign_metadata and add_sign
1 parent 4016b24 commit 476f32f

File tree

7 files changed

+85
-7
lines changed

7 files changed

+85
-7
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,14 @@ In addition to the required settings data (idp, sp), extra settings can be defin
374374
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
375375
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
376376
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
377-
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
377+
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
378+
379+
// Algorithm that the toolkit will use on digest process. Options:
380+
// 'http://www.w3.org/2000/09/xmldsig#sha1'
381+
// 'http://www.w3.org/2001/04/xmlenc#sha256'
382+
// 'http://www.w3.org/2001/04/xmldsig-more#sha384'
383+
// 'http://www.w3.org/2001/04/xmlenc#sha512'
384+
'digestAlgorithm': "http://www.w3.org/2000/09/xmldsig#sha1"
378385
},
379386

380387
// Contact information template, it is recommended to suply a

demo-django/saml/advanced_settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"wantNameId" : true,
1111
"wantNameIdEncrypted": false,
1212
"wantAssertionsEncrypted": false,
13-
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
13+
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
14+
"digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1"
1415
},
1516
"contactPerson": {
1617
"technical": {

src/onelogin/saml2/metadata.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def builder(sp, authnsign=False, wsign=False, valid_until=None, cache_duration=N
192192
return metadata
193193

194194
@staticmethod
195-
def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1):
195+
def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1, digest_algorithm=OneLogin_Saml2_Constants.SHA1):
196196
"""
197197
Signs the metadata with the key/cert provided
198198
@@ -210,8 +210,11 @@ def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.R
210210
211211
:param sign_algorithm: Signature algorithm method
212212
:type sign_algorithm: string
213+
214+
:param digest_algorithm: Digest algorithm method
215+
:type digest_algorithm: string
213216
"""
214-
return OneLogin_Saml2_Utils.add_sign(metadata, key, cert, False, sign_algorithm)
217+
return OneLogin_Saml2_Utils.add_sign(metadata, key, cert, False, sign_algorithm, digest_algorithm)
215218

216219
@staticmethod
217220
def __add_x509_key_descriptors(root, cert, signing):

src/onelogin/saml2/settings.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ def __add_default_values(self):
292292
# Signature Algorithm
293293
self.__security.setdefault('signatureAlgorithm', OneLogin_Saml2_Constants.RSA_SHA1)
294294

295+
# Digest Algorithm
296+
self.__security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA1)
297+
295298
# AttributeStatement required by default
296299
self.__security.setdefault('wantAttributeStatement', True)
297300

@@ -638,7 +641,10 @@ def get_sp_metadata(self):
638641
cert_metadata_file
639642
)
640643

641-
metadata = OneLogin_Saml2_Metadata.sign_metadata(metadata, key_metadata, cert_metadata)
644+
signature_algorithm = self.__security['signatureAlgorithm']
645+
digest_algorithm = self.__security['digestAlgorithm']
646+
647+
metadata = OneLogin_Saml2_Metadata.sign_metadata(metadata, key_metadata, cert_metadata, signature_algorithm, digest_algorithm)
642648

643649
return metadata
644650

src/onelogin/saml2/utils.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ def decrypt_element(encrypted_data, key, debug=False):
679679
return enc_ctx.decrypt(encrypted_data)
680680

681681
@staticmethod
682-
def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1):
682+
def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1, digest_algorithm=OneLogin_Saml2_Constants.SHA1):
683683
"""
684684
Adds signature key and senders certificate to an element (Message or
685685
Assertion).
@@ -698,6 +698,12 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant
698698
699699
:param sign_algorithm: Signature algorithm method
700700
:type sign_algorithm: string
701+
702+
:param digest_algorithm: Digest algorithm method
703+
:type digest_algorithm: string
704+
705+
:returns: Signed XML
706+
:rtype: string
701707
"""
702708
if xml is None or xml == '':
703709
raise Exception('Empty string supplied as input')
@@ -728,7 +734,15 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant
728734
if elem_id:
729735
elem_id = '#' + elem_id
730736

731-
ref = xmlsec.template.add_reference(signature, xmlsec.Transform.SHA1, uri=elem_id)
737+
digest_algorithm_transform_map = {
738+
OneLogin_Saml2_Constants.SHA1: xmlsec.Transform.SHA1,
739+
OneLogin_Saml2_Constants.SHA256: xmlsec.Transform.SHA256,
740+
OneLogin_Saml2_Constants.SHA384: xmlsec.Transform.SHA384,
741+
OneLogin_Saml2_Constants.SHA512: xmlsec.Transform.SHA512
742+
}
743+
digest_algorithm_transform = digest_algorithm_transform_map.get(digest_algorithm, xmlsec.Transform.SHA1)
744+
745+
ref = xmlsec.template.add_reference(signature, digest_algorithm_transform, uri=elem_id)
732746
xmlsec.template.add_transform(ref, xmlsec.Transform.ENVELOPED)
733747
xmlsec.template.add_transform(ref, xmlsec.Transform.EXCL_C14N)
734748
key_info = xmlsec.template.ensure_key_info(signature)

tests/src/OneLogin/saml2_tests/metadata_test.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from onelogin.saml2 import compat
1313
from onelogin.saml2.metadata import OneLogin_Saml2_Metadata
1414
from onelogin.saml2.settings import OneLogin_Saml2_Settings
15+
from onelogin.saml2.constants import OneLogin_Saml2_Constants
1516

1617

1718
class OneLogin_Saml2_Metadata_Test(unittest.TestCase):
@@ -218,6 +219,7 @@ def testSignMetadata(self):
218219

219220
self.assertIn('<ds:SignedInfo>\n<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', signed_metadata)
220221
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>', signed_metadata)
222+
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>', signed_metadata)
221223
self.assertIn('<ds:Reference', signed_metadata)
222224
self.assertIn('<ds:KeyInfo>\n<ds:X509Data>\n<ds:X509Certificate>', signed_metadata)
223225

@@ -226,6 +228,26 @@ def testSignMetadata(self):
226228
exception = context.exception
227229
self.assertIn("Empty string supplied as input", str(exception))
228230

231+
signed_metadata_2 = compat.to_string(OneLogin_Saml2_Metadata.sign_metadata(metadata, key, cert, OneLogin_Saml2_Constants.RSA_SHA256, OneLogin_Saml2_Constants.SHA384))
232+
233+
self.assertIn('<md:SPSSODescriptor', signed_metadata_2)
234+
self.assertIn('entityID="http://stuff.com/endpoints/metadata.php"', signed_metadata_2)
235+
self.assertIn('AuthnRequestsSigned="false"', signed_metadata_2)
236+
self.assertIn('WantAssertionsSigned="false"', signed_metadata_2)
237+
238+
self.assertIn('<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"', signed_metadata_2)
239+
self.assertIn('Location="http://stuff.com/endpoints/endpoints/acs.php"', signed_metadata_2)
240+
self.assertIn('<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"', signed_metadata_2)
241+
self.assertIn(' Location="http://stuff.com/endpoints/endpoints/sls.php"/>', signed_metadata_2)
242+
243+
self.assertIn('<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>', signed_metadata_2)
244+
245+
self.assertIn('<ds:SignedInfo>\n<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', signed_metadata_2)
246+
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>', signed_metadata_2)
247+
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>', signed_metadata_2)
248+
self.assertIn('<ds:Reference', signed_metadata_2)
249+
self.assertIn('<ds:KeyInfo>\n<ds:X509Data>\n<ds:X509Certificate>', signed_metadata_2)
250+
229251
def testAddX509KeyDescriptors(self):
230252
"""
231253
Tests the addX509KeyDescriptors method of the OneLogin_Saml2_Metadata

tests/src/OneLogin/saml2_tests/utils_test.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,31 @@ def testAddSign(self):
713713
ds_signature_8 = res_8.firstChild.firstChild.nextSibling.firstChild.nextSibling
714714
self.assertIn('ds:Signature', ds_signature_8.tagName)
715715

716+
def testAddSignCheckAlg(self):
717+
"""
718+
Tests the add_sign method of the OneLogin_Saml2_Utils
719+
Case: Review signature & digest algorithm
720+
"""
721+
settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
722+
key = settings.get_sp_key()
723+
cert = settings.get_sp_cert()
724+
725+
xml_authn = b64decode(self.file_contents(join(self.data_path, 'requests', 'authn_request.xml.base64')))
726+
xml_authn_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert))
727+
self.assertIn('<ds:SignatureValue>', xml_authn_signed)
728+
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>', xml_authn_signed)
729+
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>', xml_authn_signed)
730+
731+
xml_authn_signed_2 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert, False, OneLogin_Saml2_Constants.RSA_SHA256, OneLogin_Saml2_Constants.SHA384))
732+
self.assertIn('<ds:SignatureValue>', xml_authn_signed_2)
733+
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>', xml_authn_signed_2)
734+
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>', xml_authn_signed_2)
735+
736+
xml_authn_signed_3 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert, False, OneLogin_Saml2_Constants.RSA_SHA384, OneLogin_Saml2_Constants.SHA512))
737+
self.assertIn('<ds:SignatureValue>', xml_authn_signed_3)
738+
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>', xml_authn_signed_3)
739+
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>', xml_authn_signed_3)
740+
716741
def testValidateSign(self):
717742
"""
718743
Tests the validate_sign method of the OneLogin_Saml2_Utils

0 commit comments

Comments
 (0)