Skip to content

Commit

Permalink
Fix #181 Add DigestMethod support. (Add sign_algorithm and digest_alg…
Browse files Browse the repository at this point in the history
…orithm parameters to sign_metadata and add_sign
  • Loading branch information
pitbulk committed Mar 11, 2017
1 parent 1621567 commit a5423c3
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 9 deletions.
9 changes: 8 additions & 1 deletion README.md
Expand Up @@ -399,7 +399,14 @@ In addition to the required settings data (idp, sp), extra settings can be defin
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",

// Algorithm that the toolkit will use on digest process. Options:
// 'http://www.w3.org/2000/09/xmldsig#sha1'
// 'http://www.w3.org/2001/04/xmlenc#sha256'
// 'http://www.w3.org/2001/04/xmldsig-more#sha384'
// 'http://www.w3.org/2001/04/xmlenc#sha512'
'digestAlgorithm' => 'http://www.w3.org/2000/09/xmldsig#sha1
},
// Contact information template, it is recommended to supply
Expand Down
3 changes: 2 additions & 1 deletion demo-bottle/saml/advanced_settings.json
Expand Up @@ -10,7 +10,8 @@
"wantNameId" : true,
"wantNameIdEncrypted": false,
"wantAssertionsEncrypted": false,
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
"digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1"
},
"contactPerson": {
"technical": {
Expand Down
3 changes: 2 additions & 1 deletion demo-django/saml/advanced_settings.json
Expand Up @@ -10,7 +10,8 @@
"wantNameId" : true,
"wantNameIdEncrypted": false,
"wantAssertionsEncrypted": false,
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
"digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1"
},
"contactPerson": {
"technical": {
Expand Down
3 changes: 2 additions & 1 deletion demo-flask/saml/advanced_settings.json
Expand Up @@ -10,7 +10,8 @@
"wantNameId" : true,
"wantNameIdEncrypted": false,
"wantAssertionsEncrypted": false,
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
"digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1"
},
"contactPerson": {
"technical": {
Expand Down
7 changes: 5 additions & 2 deletions src/onelogin/saml2/metadata.py
Expand Up @@ -202,7 +202,7 @@ def builder(sp, authnsign=False, wsign=False, valid_until=None, cache_duration=N
return metadata

@staticmethod
def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1):
def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1, digest_algorithm=OneLogin_Saml2_Constants.SHA1):
"""
Signs the metadata with the key/cert provided
Expand All @@ -218,10 +218,13 @@ def sign_metadata(metadata, key, cert, sign_algorithm=OneLogin_Saml2_Constants.R
:param sign_algorithm: Signature algorithm method
:type sign_algorithm: string
:param digest_algorithm: Digest algorithm method
:type digest_algorithm: string
:returns: Signed Metadata
:rtype: string
"""
return OneLogin_Saml2_Utils.add_sign(metadata, key, cert, False, sign_algorithm)
return OneLogin_Saml2_Utils.add_sign(metadata, key, cert, False, sign_algorithm, digest_algorithm)

@staticmethod
def add_x509_key_descriptors(metadata, cert=None):
Expand Down
8 changes: 7 additions & 1 deletion src/onelogin/saml2/settings.py
Expand Up @@ -283,6 +283,9 @@ def __add_default_values(self):
# Signature Algorithm
self.__security.setdefault('signatureAlgorithm', OneLogin_Saml2_Constants.RSA_SHA1)

# Digest Algorithm
self.__security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA1)

# AttributeStatement required by default
self.__security.setdefault('wantAttributeStatement', True)

Expand Down Expand Up @@ -639,7 +642,10 @@ def get_sp_metadata(self):
cert_metadata_file
)

metadata = OneLogin_Saml2_Metadata.sign_metadata(metadata, key_metadata, cert_metadata)
signature_algorithm = self.__security['signatureAlgorithm']
digest_algorithm = self.__security['digestAlgorithm']

metadata = OneLogin_Saml2_Metadata.sign_metadata(metadata, key_metadata, cert_metadata, signature_algorithm, digest_algorithm)

return metadata

Expand Down
18 changes: 16 additions & 2 deletions src/onelogin/saml2/utils.py
Expand Up @@ -797,7 +797,7 @@ def write_temp_file(content):
return f_temp

@staticmethod
def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1):
def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constants.RSA_SHA1, digest_algorithm=OneLogin_Saml2_Constants.SHA1):
"""
Adds signature key and senders certificate to an element (Message or
Assertion).
Expand All @@ -816,6 +816,12 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant
:param sign_algorithm: Signature algorithm method
:type sign_algorithm: string
:param digest_algorithm: Digest algorithm method
:type digest_algorithm: string
:returns: Signed XML
:rtype: string
"""
if xml is None or xml == '':
raise Exception('Empty string supplied as input')
Expand Down Expand Up @@ -866,7 +872,15 @@ def add_sign(xml, key, cert, debug=False, sign_algorithm=OneLogin_Saml2_Constant
else:
elem[0].insert(0, signature)

ref = signature.addReference(xmlsec.TransformSha1)
digest_algorithm_transform_map = {
OneLogin_Saml2_Constants.SHA1: xmlsec.TransformSha1,
OneLogin_Saml2_Constants.SHA256: xmlsec.TransformSha256,
OneLogin_Saml2_Constants.SHA384: xmlsec.TransformSha384,
OneLogin_Saml2_Constants.SHA512: xmlsec.TransformSha512
}
digest_algorithm_transform = digest_algorithm_transform_map.get(digest_algorithm, xmlsec.TransformSha1)

ref = signature.addReference(digest_algorithm_transform)
ref.addTransform(xmlsec.TransformEnveloped)
ref.addTransform(xmlsec.TransformExclC14N)

Expand Down
21 changes: 21 additions & 0 deletions tests/src/OneLogin/saml2_tests/metadata_test.py
Expand Up @@ -14,6 +14,7 @@

from onelogin.saml2.metadata import OneLogin_Saml2_Metadata
from onelogin.saml2.settings import OneLogin_Saml2_Settings
from onelogin.saml2.constants import OneLogin_Saml2_Constants


class OneLogin_Saml2_Metadata_Test(unittest.TestCase):
Expand Down Expand Up @@ -222,12 +223,32 @@ def testSignMetadata(self):

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

with self.assertRaisesRegexp(Exception, 'Empty string supplied as input'):
OneLogin_Saml2_Metadata.sign_metadata('', key, cert)

signed_metadata_2 = OneLogin_Saml2_Metadata.sign_metadata(metadata, key, cert, OneLogin_Saml2_Constants.RSA_SHA256, OneLogin_Saml2_Constants.SHA384)
self.assertIn('<md:SPSSODescriptor', signed_metadata_2)
self.assertIn('entityID="http://stuff.com/endpoints/metadata.php"', signed_metadata_2)
self.assertIn('AuthnRequestsSigned="false"', signed_metadata_2)
self.assertIn('WantAssertionsSigned="false"', signed_metadata_2)

self.assertIn('<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"', signed_metadata_2)
self.assertIn('Location="http://stuff.com/endpoints/endpoints/acs.php"', signed_metadata_2)
self.assertIn('<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"', signed_metadata_2)
self.assertIn(' Location="http://stuff.com/endpoints/endpoints/sls.php"/>', signed_metadata_2)

self.assertIn('<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>', signed_metadata_2)

self.assertIn('<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', signed_metadata_2)
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>', signed_metadata_2)
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>', signed_metadata_2)
self.assertIn('<ds:Reference', signed_metadata_2)
self.assertIn('<ds:KeyInfo><ds:X509Data>\n<ds:X509Certificate>', signed_metadata_2)

def testAddX509KeyDescriptors(self):
"""
Tests the addX509KeyDescriptors method of the OneLogin_Saml2_Metadata
Expand Down
28 changes: 28 additions & 0 deletions tests/src/OneLogin/saml2_tests/utils_test.py
Expand Up @@ -810,6 +810,34 @@ def testAddSign(self):
with self.assertRaisesRegexp(Exception, 'Error parsing xml string'):
OneLogin_Saml2_Utils.add_sign(1, key, cert)

def testAddSignCheckAlg(self):
"""
Tests the add_sign method of the OneLogin_Saml2_Utils
Case: Review signature & digest algorithm
"""
settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
key = settings.get_sp_key()
cert = settings.get_sp_cert()

xml_authn = b64decode(self.file_contents(join(self.data_path, 'requests', 'authn_request.xml.base64')))
xml_authn_signed = OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert)
self.assertIn('<ds:SignatureValue>', xml_authn_signed)
self.assertIn('<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', xml_authn_signed)
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>', xml_authn_signed)
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>', xml_authn_signed)

xml_authn_signed_2 = OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert, False, OneLogin_Saml2_Constants.RSA_SHA256, OneLogin_Saml2_Constants.SHA384)
self.assertIn('<ds:SignatureValue>', xml_authn_signed_2)
self.assertIn('<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', xml_authn_signed_2)
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>', xml_authn_signed_2)
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>', xml_authn_signed_2)

xml_authn_signed_3 = OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert, False, OneLogin_Saml2_Constants.RSA_SHA384, OneLogin_Saml2_Constants.SHA512)
self.assertIn('<ds:SignatureValue>', xml_authn_signed_3)
self.assertIn('<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', xml_authn_signed_3)
self.assertIn('<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>', xml_authn_signed_3)
self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>', xml_authn_signed_3)

def testValidateSign(self):
"""
Tests the validate_sign method of the OneLogin_Saml2_Utils
Expand Down

0 comments on commit a5423c3

Please sign in to comment.