saml2 3.0.8 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/saml2/key.rb +38 -4
- data/lib/saml2/response.rb +9 -4
- data/lib/saml2/service_provider.rb +2 -0
- data/lib/saml2/signable.rb +27 -24
- data/lib/saml2/version.rb +1 -1
- data/spec/fixtures/identity_provider.xml +1 -1
- data/spec/fixtures/response_assertion_signed_reffed_from_response.xml +6 -0
- data/spec/fixtures/response_with_rsa_key_value.xml +1 -0
- data/spec/fixtures/service_provider.xml +1 -1
- data/spec/lib/attribute_consuming_service_spec.rb +2 -0
- data/spec/lib/attribute_spec.rb +2 -0
- data/spec/lib/authn_request_spec.rb +2 -0
- data/spec/lib/bindings/http_redirect_spec.rb +4 -2
- data/spec/lib/conditions_spec.rb +2 -0
- data/spec/lib/entity_spec.rb +2 -0
- data/spec/lib/identity_provider_spec.rb +6 -0
- data/spec/lib/indexed_object_spec.rb +2 -0
- data/spec/lib/key_spec.rb +12 -0
- data/spec/lib/logout_request_spec.rb +2 -0
- data/spec/lib/logout_response_spec.rb +2 -0
- data/spec/lib/message_spec.rb +2 -0
- data/spec/lib/response_spec.rb +16 -0
- data/spec/lib/service_provider_spec.rb +7 -0
- data/spec/lib/signable_spec.rb +15 -0
- data/spec/spec_helper.rb +3 -0
- metadata +18 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a9f01162da7309af02b28de55593de014c32eba85d87a7af95360423cd03550
|
4
|
+
data.tar.gz: 71b3e1f1309c840e649e8d6a3dfad701b01349c5c8b3c3378b96733ef62718cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b8ff067a719d4e450a94b23967b6519a2c51ed806decb7b342f06c29ace840625b2d03056a986fbd54c12fe9adc2997ab24731be40341d52f01d34d5e14f220
|
7
|
+
data.tar.gz: 2de7817d5dda92bacd3e87fe302a7f7f35e70ca7a2234b4237a45951456ab19c66da8ef836c9004ae289f4d7f8d38ef47ff4f6efc21a2d641bc8d0c62a00f1df
|
data/lib/saml2/key.rb
CHANGED
@@ -11,6 +11,8 @@ module SAML2
|
|
11
11
|
class KeyInfo < Base
|
12
12
|
# @return [String] The PEM encoded certificate.
|
13
13
|
attr_reader :x509
|
14
|
+
# @return [OpenSSL::PKey::PKey] An RSA Public Key
|
15
|
+
attr_accessor :key
|
14
16
|
|
15
17
|
# @param x509 [String] The PEM encoded certificate.
|
16
18
|
def initialize(x509 = nil)
|
@@ -19,7 +21,15 @@ module SAML2
|
|
19
21
|
|
20
22
|
# (see Base#from_xml)
|
21
23
|
def from_xml(node)
|
22
|
-
self.x509 = node.at_xpath('dsig:
|
24
|
+
self.x509 = node.at_xpath('dsig:X509Data/dsig:X509Certificate', Namespaces::ALL)&.content&.strip
|
25
|
+
if (rsa_key_value = node.at_xpath('dsig:KeyValue/dsig:RSAKeyValue', Namespaces::ALL))
|
26
|
+
modulus = crypto_binary_to_integer(rsa_key_value.at_xpath('dsig:Modulus', Namespaces::ALL)&.content&.strip)
|
27
|
+
exponent = crypto_binary_to_integer(rsa_key_value.at_xpath('dsig:Exponent', Namespaces::ALL)&.content&.strip)
|
28
|
+
if modulus && exponent
|
29
|
+
@key = OpenSSL::PKey::RSA.new
|
30
|
+
key.set_key(modulus, exponent, nil)
|
31
|
+
end
|
32
|
+
end
|
23
33
|
end
|
24
34
|
|
25
35
|
def x509=(value)
|
@@ -28,9 +38,15 @@ module SAML2
|
|
28
38
|
|
29
39
|
# @return [OpenSSL::X509::Certificate]
|
30
40
|
def certificate
|
41
|
+
return nil if x509.nil?
|
31
42
|
@certificate ||= OpenSSL::X509::Certificate.new(Base64.decode64(x509))
|
32
43
|
end
|
33
44
|
|
45
|
+
# @return [OpenSSL::PKey::PKey]
|
46
|
+
def public_key
|
47
|
+
key || certificate&.public_key
|
48
|
+
end
|
49
|
+
|
34
50
|
# Formats a fingerprint as all lowercase, with a : every two characters,
|
35
51
|
# stripping all non-hexadecimal characters.
|
36
52
|
# @param fingerprint [String]
|
@@ -41,17 +57,35 @@ module SAML2
|
|
41
57
|
|
42
58
|
# @return [String]
|
43
59
|
def fingerprint
|
60
|
+
return nil unless certificate
|
44
61
|
@fingerprint ||= self.class.format_fingerprint(Digest::SHA1.hexdigest(certificate.to_der))
|
45
62
|
end
|
46
63
|
|
47
64
|
# (see Base#build)
|
48
65
|
def build(builder)
|
49
66
|
builder['dsig'].KeyInfo do |key_info|
|
50
|
-
|
51
|
-
|
67
|
+
if x509
|
68
|
+
key_info['dsig'].X509Data do |x509_data|
|
69
|
+
x509_data['dsig'].X509Certificate(x509)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
if key.is_a?(OpenSSL::PKey::RSA)
|
73
|
+
key_info['dsig'].KeyValue do |key_value|
|
74
|
+
key_value['dsig'].RSAKeyValue do |rsa_key_value|
|
75
|
+
rsa_key_value['dsig'].Modulus(Base64.encode64(key.n.to_s(2)))
|
76
|
+
rsa_key_value['dsig'].Exponent(Base64.encode64(key.e.to_s(2)))
|
77
|
+
end
|
78
|
+
end
|
52
79
|
end
|
53
80
|
end
|
54
81
|
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def crypto_binary_to_integer(str)
|
86
|
+
return nil unless str
|
87
|
+
OpenSSL::BN.new(Base64.decode64(str), 2)
|
88
|
+
end
|
55
89
|
end
|
56
90
|
|
57
91
|
class KeyDescriptor < KeyInfo
|
@@ -99,7 +133,7 @@ module SAML2
|
|
99
133
|
|
100
134
|
# (see Base#from_xml)
|
101
135
|
def from_xml(node)
|
102
|
-
super
|
136
|
+
super(node.at_xpath('dsig:KeyInfo', Namespaces::ALL))
|
103
137
|
self.use = node['use']
|
104
138
|
self.encryption_methods = load_object_array(node, 'md:EncryptionMethod', EncryptionMethod)
|
105
139
|
end
|
data/lib/saml2/response.rb
CHANGED
@@ -13,6 +13,8 @@ module SAML2
|
|
13
13
|
attr_reader :assertions
|
14
14
|
|
15
15
|
# Respond to an {AuthnRequest}
|
16
|
+
#
|
17
|
+
# {AuthnRequest#resolve} needs to have been previously called on the {AuthnRequest}.
|
16
18
|
# @param authn_request [AuthnRequest]
|
17
19
|
# @param issuer [NameID]
|
18
20
|
# @param name_id [NameID] The Subject
|
@@ -125,14 +127,16 @@ module SAML2
|
|
125
127
|
return errors
|
126
128
|
end
|
127
129
|
|
128
|
-
certificates = idp.signing_keys.map(&:certificate)
|
129
|
-
|
130
|
+
certificates = idp.signing_keys.map(&:certificate).compact
|
131
|
+
keys = idp.signing_keys.map(&:key).compact
|
132
|
+
if idp.fingerprints.empty? && certificates.empty? && keys.empty?
|
130
133
|
errors << "could not find certificate to validate message"
|
131
134
|
return errors
|
132
135
|
end
|
133
136
|
|
134
137
|
if signed?
|
135
|
-
unless (signature_errors = validate_signature(
|
138
|
+
unless (signature_errors = validate_signature(key: keys,
|
139
|
+
fingerprint: idp.fingerprints,
|
136
140
|
cert: certificates)).empty?
|
137
141
|
return errors.concat(signature_errors)
|
138
142
|
end
|
@@ -143,7 +147,8 @@ module SAML2
|
|
143
147
|
|
144
148
|
# this might be nil, if the assertion was encrypted
|
145
149
|
if assertion&.signed?
|
146
|
-
unless (signature_errors = assertion.validate_signature(
|
150
|
+
unless (signature_errors = assertion.validate_signature(key: keys,
|
151
|
+
fingerprint: idp.fingerprints,
|
147
152
|
cert: certificates)).empty?
|
148
153
|
return errors.concat(signature_errors)
|
149
154
|
end
|
data/lib/saml2/signable.rb
CHANGED
@@ -7,16 +7,16 @@ module SAML2
|
|
7
7
|
# @return [Nokogiri::XML::Element, nil]
|
8
8
|
def signature
|
9
9
|
unless instance_variable_defined?(:@signature)
|
10
|
-
@signature = xml.
|
11
|
-
|
12
|
-
signed_node = @signature.at_xpath('dsig:SignedInfo/dsig:Reference', Namespaces::ALL)['URI']
|
10
|
+
@signature = xml.xpath('//dsig:Signature', Namespaces::ALL).find do |signature|
|
11
|
+
signed_node = signature.at_xpath('dsig:SignedInfo/dsig:Reference', Namespaces::ALL)['URI']
|
13
12
|
if signed_node == ''
|
14
|
-
|
13
|
+
true if xml == xml.document.root
|
15
14
|
elsif signed_node != "##{xml['ID']}"
|
16
|
-
|
15
|
+
false
|
17
16
|
else
|
18
17
|
# validating the schema will automatically add ID attributes, so check that first
|
19
18
|
xml.set_id_attribute('ID') unless xml.document.get_id(xml['ID'])
|
19
|
+
true
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -27,7 +27,7 @@ module SAML2
|
|
27
27
|
def signing_key
|
28
28
|
unless instance_variable_defined?(:@signing_key)
|
29
29
|
# don't use `... if signature.at_xpath(...)` - we need to make sure we assign the nil
|
30
|
-
@signing_key = signature.at_xpath('dsig:KeyInfo', Namespaces::ALL) ? KeyInfo.from_xml(
|
30
|
+
@signing_key = (key_info = signature.at_xpath('dsig:KeyInfo', Namespaces::ALL)) ? KeyInfo.from_xml(key_info) : nil
|
31
31
|
end
|
32
32
|
@signing_key
|
33
33
|
end
|
@@ -38,20 +38,21 @@ module SAML2
|
|
38
38
|
|
39
39
|
# Validate the signature on this object.
|
40
40
|
#
|
41
|
-
# At least one of +key+, +fingerprint+ or +cert+ must be provided.
|
41
|
+
# At least one of +key+, +fingerprint+ or +cert+ must be provided. If the signature
|
42
|
+
# doesn't specify which key to use, the first provided key will be used.
|
42
43
|
#
|
43
|
-
# @param key optional [String, OpenSSL::PKey::PKey]
|
44
|
-
#
|
44
|
+
# @param key optional [String, OpenSSL::PKey::PKey, Array<String>, Array<OpenSSL::PKey::PKey>]
|
45
|
+
# Public keys that are allowed to be the valid key.
|
45
46
|
# @param fingerprint optional [Array<String>, String]
|
46
47
|
# SHA1 fingerprints of trusted certificates. If provided, they will be
|
47
48
|
# checked against the {#signing_key} embedded in the {#signature}, and if
|
48
|
-
# a match is found, the
|
49
|
-
#
|
49
|
+
# a match is found, the key embedded in the signature will be
|
50
|
+
# used for verifying the signature.
|
50
51
|
# @param cert optional [Array<String>, String]
|
51
52
|
# A single or array of trusted certificates. If provided, they will be
|
52
|
-
#
|
53
|
-
# a match
|
54
|
-
#
|
53
|
+
# checked against the {#signing_key} embedded in the {#signature}, and if
|
54
|
+
# a match is found, the key embedded in the signature will be used for
|
55
|
+
# verifying the signature.
|
55
56
|
# @return [Array<String>] An empty array on success, details of errors on failure.
|
56
57
|
def validate_signature(key: nil,
|
57
58
|
fingerprint: nil,
|
@@ -67,23 +68,25 @@ module SAML2
|
|
67
68
|
end
|
68
69
|
certs = certs.uniq
|
69
70
|
|
70
|
-
trusted_keys =
|
71
|
+
trusted_keys = Array.wrap(key).map(&:to_s)
|
72
|
+
trusted_keys.concat(certs.map do |cert|
|
71
73
|
cert = cert.is_a?(String) ? OpenSSL::X509::Certificate.new(cert) : cert
|
72
74
|
cert.public_key.to_s
|
73
|
-
end
|
74
|
-
|
75
|
-
|
75
|
+
end)
|
76
|
+
|
77
|
+
if trusted_keys.include?(signing_key&.public_key&.to_s)
|
78
|
+
verification_key = signing_key.public_key.to_s
|
76
79
|
end
|
77
80
|
# signature doesn't say who signed it. hope and pray it's with the only certificate
|
78
81
|
# we know about
|
79
|
-
if signing_key.nil?
|
80
|
-
|
82
|
+
if signing_key.nil?
|
83
|
+
verification_key = trusted_keys.first
|
81
84
|
end
|
82
85
|
|
83
|
-
return ["no trusted signing key found"] if
|
86
|
+
return ["no trusted signing key found"] if verification_key.nil?
|
84
87
|
|
85
88
|
begin
|
86
|
-
result = signature.verify_with(key:
|
89
|
+
result = signature.verify_with(key: verification_key)
|
87
90
|
result ? [] : ["signature is invalid"]
|
88
91
|
rescue XMLSec::VerificationError => e
|
89
92
|
[e.message]
|
@@ -96,8 +99,8 @@ module SAML2
|
|
96
99
|
#
|
97
100
|
# @param (see #validate_signature)
|
98
101
|
# @return [Boolean]
|
99
|
-
def valid_signature?(
|
100
|
-
validate_signature(
|
102
|
+
def valid_signature?(**kwargs)
|
103
|
+
validate_signature(**kwargs).empty?
|
101
104
|
end
|
102
105
|
|
103
106
|
# Sign this object.
|
data/lib/saml2/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
<?xml version="1.0"?>
|
2
2
|
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://sso.school.edu/idp/shibboleth">
|
3
|
-
<IDPSSODescriptor protocolSupportEnumeration="urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
|
3
|
+
<IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
|
4
4
|
<KeyDescriptor use="signing">
|
5
5
|
<ds:KeyInfo>
|
6
6
|
<ds:X509Data>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<samlp:Response ID="eppcgfbmldefddomokfgiljnkflhppmoflakahld" IssueInstant="2020-08-11T18:19:49Z" Destination="https://wscc.instructure.com/login/saml" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><Reference URI="#enmnbnkdhfhnbjeifihomffcoanmnjdaocnhgnhc"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><DigestValue>cyBkaF5MxEOSX9hLm0g/BWMJpQA=</DigestValue></Reference></SignedInfo><SignatureValue>BqXuyorfBboZI3sSSi4PC3GnJMKyLSQ/897M1RYmgVHx8Pbg1ANy75mpjRQQxGOIz/nSTh6eTPkkFEAT34nhxBSd+JfHof0RfLl/lBI1klSmpi/YoHCKLdVt+iwAemmBNw5Rxw59EepgrbcVtgjsjWISdvMyY7Wqb3nyJDwTGWw=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>yPxoJ9DLOTzn9j91xlqGTX/8Hs5hxjImPalS9qTOc6BYJgXSC7HtxBLMc0usJG58/OaHgWFlaDi4HSBlZe2vLzecaWL1HYxJtW6s+UpD5i+uoxGTPM1ITNlZudGQblh3XTUESrPUZVwSt1N+Vqd4AUHux0E078meTqj9+EMcgsk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIIB4TCCAU6gAwIBAgIQhv64tDcg/45BI6qmDbJfKDAJBgUrDgMCHQUAMA8xDTALBgNVBAMTBFRFU1QwIBcNMjAwMTI3MTkxNzMxWhgPMjA4MDEyMzEwNTAwMDBaMA8xDTALBgNVBAMTBFRFU1QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMj8aCfQyzk85/Y/dcZahk1//B7OYcYyJj2pUvakznOgWCYF0gux7cQSzHNLrCRufPzmh4FhZWg4uB0gZWXtry83nGli9R2MSbVurPlKQ+YvrqMRkzzNSEzZWbnRkG5Yd101BEqz1GVcErdTflaneAFB7sdBNO/Jnk6o/fhDHILJAgMBAAGjRDBCMEAGA1UdAQQ5MDeAEFm8dl7/zBigioh82gZb6WGhETAPMQ0wCwYDVQQDEwRURVNUghCG/ri0NyD/jkEjqqYNsl8oMAkGBSsOAwIdBQADgYEAotOROUrAiZr7oA3iaZLxq+B6sN+JdWSBquvDUzaMgIWRvUBZPqmOKpXK0+XSLXChgklpVXBXAo78Juy0zza/ZAMyGPbYlSZSME6GlApjp8hi6wi0ti/usi/D8SQSJ9ephwz2JAvI5WP16PzIruYUlf3uI72hKT0NW8Pl3PhT8z8=</X509Certificate></X509Data></KeyInfo></Signature><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></samlp:Status><Assertion ID="enmnbnkdhfhnbjeifihomffcoanmnjdaocnhgnhc" IssueInstant="2020-08-11T18:19:49Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>
|
2
|
+
https://my.wscc.edu/idp
|
3
|
+
</Issuer><Subject><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">narnold@wscc.edu</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData Recipient="" NotOnOrAfter="2020-08-11T18:29:49Z" InResponseTo="_bd878908-34c0-4e6e-b429-90cc8bfae27c" /></SubjectConfirmation></Subject><Conditions NotBefore="2020-08-11T18:14:49Z" NotOnOrAfter="2020-08-11T18:29:49Z"><AudienceRestriction><Audience>http://wscc.instructure.com/saml2</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="email"><AttributeValue>narnold@wscc.edu</AttributeValue></Attribute><Attribute Name="display_name"><AttributeValue>Nicholas Arnold</AttributeValue></Attribute><Attribute Name="given_name"><AttributeValue>Nicholas</AttributeValue></Attribute><Attribute Name="integration_id"><AttributeValue>Ed18RSTYO0ivqnZuzQPehQ==</AttributeValue></Attribute><Attribute Name="sis_user_id"><AttributeValue>0097365</AttributeValue></Attribute><Attribute Name="sortable_name"><AttributeValue>Arnold, Nicholas</AttributeValue></Attribute><Attribute Name="surname"><AttributeValue>Arnold</AttributeValue></Attribute><Attribute Name="time_zone"><AttributeValue>US/Eastern</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="2020-08-11T18:19:49Z"><AuthnContext><AuthnContextClassRef>
|
4
|
+
urn:oasis:names:tc:SAML:2.0:ac:classes:Password
|
5
|
+
</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion></samlp:Response>
|
6
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
<Response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_f0b41a2d2b524b8c92027ef426b3cf3b" Version="2.0" IssueInstant="2021-08-02T16:53:20Z" Destination="https://whartononline.instructure.com/login/saml" xmlns="urn:oasis:names:tc:SAML:2.0:protocol"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://test.pep.siemens.knowledgeanywhere.com</Issuer><Status><StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></Status><Assertion Version="2.0" ID="_8be8f34e76e14fe3a990dcc00e60d13d" IssueInstant="2021-08-02T16:53:20Z" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Issuer>https://test.pep.siemens.knowledgeanywhere.com</Issuer><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><Reference URI="#_8be8f34e76e14fe3a990dcc00e60d13d"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><DigestValue>VQGa66oHsKF7zCXzob8WTCBLHUI=</DigestValue></Reference></SignedInfo><SignatureValue>S+He/m2bwH+QiGmR/eH9K71TXCQ2hC8BI5pgKWyN3gFfCfEvUYgnksXu/oE1wVxvpp2dtWPiT/8vejTJWfl4Yz8VT8gDcH9xKWhPzsHba8qCHui3U486dR1SwkwII3H0cK0Xhr/dKvco85QPAUsZTKZ+dq5HdeLu6n6aERzC/136iuw/eyCFDfFS2E3dfTHd2u+E9uUh6wI/nCpSzxMxQw9Wz9i941jdLKovuqILEVbbV33sg9VZRFNAs+D92YYiegKnnBBP2+H2fXFA2Wc5uP8yAAsrFMvHA7R1tIP7jci8vEplK/Hlkdta8KQVq4fIKsXUeYu17uNFfbSIXkL5oQ==</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>vyYuRxa/PI/r3N4lxOxL/O1OtwYexfmY3mF6mvjMr4YD762UZWNNhLZsFaWqUs3HQQwAZCg0sU65s4a+t4XANuRX1k3E1oPHBmqfLZ8spBeRfyHxc0KSah10RFfCO1LZdte2Avtpvh5RMkGkMhbWFAYQmc24u6sHvI1OVUkKmBiwss6QJAi6bttMsp0VNuW7TmmsPjxmaq5dddSryLHpVP/VbnnLu4fWOnLBMTtDY37l6ZAyqKW8PEaXWSFJSjGlA+9D76+bf5C+snk0yooARZduPny5biXtoHbC3DnPDBbCN70bvkxGNqMBiqPQrI2B7zZUXA1nDiK/iHPxl5X3ZQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue></KeyInfo></Signature><Subject><NameID>chad@instructure.com</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData Recipient="https://whartononline.instructure.com/login/saml" NotOnOrAfter="2021-08-02T16:58:20Z" /></SubjectConfirmation></Subject><Conditions NotBefore="2021-08-02T16:48:20Z" NotOnOrAfter="2021-08-02T16:58:20Z"><AudienceRestriction><Audience>http://whartononline.instructure.com/saml2</Audience></AudienceRestriction></Conditions><AuthnStatement AuthnInstant="2021-08-02T16:53:20Z"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement><AttributeStatement><Attribute Name="FIRST_NAME"><AttributeValue>Chad</AttributeValue></Attribute><Attribute Name="LAST_NAME"><AttributeValue>Broadhead</AttributeValue></Attribute><Attribute Name="EMAIL"><AttributeValue>chad@instructure.com</AttributeValue></Attribute><Attribute Name="UserGuid"><AttributeValue>c555db14-fc81-4836-98de-62f592f36ede</AttributeValue></Attribute></AttributeStatement></Assertion></Response>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<?xml version="1.0"?>
|
2
2
|
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://siteadmin.instructure.com/saml2" ID="unique">
|
3
|
-
<SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
3
|
+
<SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
4
4
|
|
5
5
|
<KeyDescriptor use="encryption">
|
6
6
|
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
|
data/spec/lib/attribute_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../../spec_helper'
|
2
4
|
|
3
5
|
require 'openssl'
|
@@ -86,10 +88,10 @@ module SAML2
|
|
86
88
|
end
|
87
89
|
|
88
90
|
it "raises on unsupported signature algorithm" do
|
89
|
-
x = url
|
91
|
+
x = url.dup
|
90
92
|
# SigAlg is now sha10
|
91
93
|
x << "0"
|
92
|
-
expect { Bindings::HTTPRedirect.decode(
|
94
|
+
expect { Bindings::HTTPRedirect.decode(x, public_key: certificate) }.to raise_error(UnsupportedSignatureAlgorithm)
|
93
95
|
end
|
94
96
|
|
95
97
|
it "allows the caller to detect an unsigned message" do
|
data/spec/lib/conditions_spec.rb
CHANGED
data/spec/lib/entity_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../spec_helper'
|
2
4
|
|
3
5
|
module SAML2
|
@@ -32,6 +34,10 @@ module SAML2
|
|
32
34
|
it "should find the signing certificate" do
|
33
35
|
expect(idp.keys.first.x509).to match(/MIIE8TCCA9mgAwIBAgIJAITusxON60cKMA0GCSqGSIb3DQEBBQUAMIGrMQswCQYD/)
|
34
36
|
end
|
37
|
+
|
38
|
+
it "loads identity provider attributes" do
|
39
|
+
expect(idp.want_authn_requests_signed?).to be_truthy
|
40
|
+
end
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|
data/spec/lib/key_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../spec_helper'
|
2
4
|
|
3
5
|
module SAML2
|
@@ -7,5 +9,15 @@ module SAML2
|
|
7
9
|
expect(KeyInfo.format_fingerprint("\u200F abcdefghijklmnop 1234567890-\n a1")).to eq("ab:cd:ef:12:34:56:78:90:a1")
|
8
10
|
end
|
9
11
|
end
|
12
|
+
|
13
|
+
describe '#certificate' do
|
14
|
+
it "doesn't asplode if the keyinfo is just an rsa key value" do
|
15
|
+
response = Nokogiri::XML(fixture("response_with_rsa_key_value.xml"))
|
16
|
+
key = KeyInfo.from_xml(response.at_xpath('//dsig:KeyInfo', Namespaces::ALL))
|
17
|
+
expect(key.certificate).to be_nil
|
18
|
+
expect(key.fingerprint).to be_nil
|
19
|
+
expect(key.key).not_to be_nil
|
20
|
+
end
|
21
|
+
end
|
10
22
|
end
|
11
23
|
end
|
data/spec/lib/message_spec.rb
CHANGED
data/spec/lib/response_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../spec_helper'
|
2
4
|
|
3
5
|
module SAML2
|
@@ -272,6 +274,20 @@ MIIB/jCCAWegAwIBAgIBCjANBgkqhkiG9w0BAQQFADAkMSIwIAYDVQQDExlhZGRlcjEuaXRzLnVuaW1l
|
|
272
274
|
expect(response.errors).to eq []
|
273
275
|
expect(response.assertions.first.subject.name_id.id).to eq 'testuserint.sso@staff.oimtest.unimelb.edu.au'
|
274
276
|
end
|
277
|
+
|
278
|
+
it "finds signatures the sign the assertion, not inside the assertion" do
|
279
|
+
response = Response.parse(fixture("response_assertion_signed_reffed_from_response.xml"))
|
280
|
+
sp_entity.entity_id = 'http://wscc.instructure.com/saml2'
|
281
|
+
idp_entity.entity_id = 'https://my.wscc.edu/idp'
|
282
|
+
idp_entity.identity_providers.first.keys.clear
|
283
|
+
idp_entity.identity_providers.first.fingerprints << "c4f473274116a3cbc295c3abf77c7ed1ade9b904"
|
284
|
+
|
285
|
+
sp_entity.valid_response?(response, idp_entity, verification_time: response.issue_instant)
|
286
|
+
expect(response.errors).to eq []
|
287
|
+
expect(response.assertions.first.subject.name_id.id).to eq 'narnold@wscc.edu'
|
288
|
+
expect(response).not_to be_signed
|
289
|
+
expect(response.assertions.first).to be_signed
|
290
|
+
end
|
275
291
|
end
|
276
292
|
end
|
277
293
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../spec_helper'
|
2
4
|
|
3
5
|
module SAML2
|
@@ -64,6 +66,11 @@ module SAML2
|
|
64
66
|
expect(sp.keys.first.encryption_methods.first.algorithm).to eq KeyDescriptor::EncryptionMethod::Algorithm::AES128_CBC
|
65
67
|
expect(sp.keys.first.encryption_methods.first.key_size).to eq 128
|
66
68
|
end
|
69
|
+
|
70
|
+
it "loads service provider attributes" do
|
71
|
+
expect(sp.authn_requests_signed?).to be_truthy
|
72
|
+
expect(sp.want_assertions_signed?).to be_truthy
|
73
|
+
end
|
67
74
|
end
|
68
75
|
end
|
69
76
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
module SAML2
|
6
|
+
describe Signable do
|
7
|
+
describe '#valid_signature?' do
|
8
|
+
it 'can work with an explicit key from metadata' do
|
9
|
+
response = Response.parse(fixture("response_with_rsa_key_value.xml"))
|
10
|
+
key = response.assertions.first.signing_key.key
|
11
|
+
expect(response.assertions.first.valid_signature?(key: [key])).to eq true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saml2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: 1.5.8
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '1.
|
22
|
+
version: '1.12'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: 1.5.8
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '1.
|
32
|
+
version: '1.12'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: nokogiri-xmlsec-instructure
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,7 +59,7 @@ dependencies:
|
|
59
59
|
version: '3.2'
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '6.
|
62
|
+
version: '6.2'
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -69,21 +69,21 @@ dependencies:
|
|
69
69
|
version: '3.2'
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: '6.
|
72
|
+
version: '6.2'
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
74
|
name: byebug
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|
76
76
|
requirements:
|
77
77
|
- - "~>"
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: '
|
79
|
+
version: '11.0'
|
80
80
|
type: :development
|
81
81
|
prerelease: false
|
82
82
|
version_requirements: !ruby/object:Gem::Requirement
|
83
83
|
requirements:
|
84
84
|
- - "~>"
|
85
85
|
- !ruby/object:Gem::Version
|
86
|
-
version: '
|
86
|
+
version: '11.0'
|
87
87
|
- !ruby/object:Gem::Dependency
|
88
88
|
name: rake
|
89
89
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,11 +191,13 @@ files:
|
|
191
191
|
- spec/fixtures/noconditions_response.xml
|
192
192
|
- spec/fixtures/othercertificate.pem
|
193
193
|
- spec/fixtures/privatekey.key
|
194
|
+
- spec/fixtures/response_assertion_signed_reffed_from_response.xml
|
194
195
|
- spec/fixtures/response_signed.xml
|
195
196
|
- spec/fixtures/response_tampered_certificate.xml
|
196
197
|
- spec/fixtures/response_tampered_signature.xml
|
197
198
|
- spec/fixtures/response_with_attribute_signed.xml
|
198
199
|
- spec/fixtures/response_with_encrypted_assertion.xml
|
200
|
+
- spec/fixtures/response_with_rsa_key_value.xml
|
199
201
|
- spec/fixtures/response_with_signed_assertion_and_encrypted_subject.xml
|
200
202
|
- spec/fixtures/response_without_keyinfo.xml
|
201
203
|
- spec/fixtures/service_provider.xml
|
@@ -221,12 +223,13 @@ files:
|
|
221
223
|
- spec/lib/message_spec.rb
|
222
224
|
- spec/lib/response_spec.rb
|
223
225
|
- spec/lib/service_provider_spec.rb
|
226
|
+
- spec/lib/signable_spec.rb
|
224
227
|
- spec/spec_helper.rb
|
225
228
|
homepage: https://github.com/instructure/ruby-saml2
|
226
229
|
licenses:
|
227
230
|
- MIT
|
228
231
|
metadata: {}
|
229
|
-
post_install_message:
|
232
|
+
post_install_message:
|
230
233
|
rdoc_options: []
|
231
234
|
require_paths:
|
232
235
|
- lib
|
@@ -241,8 +244,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
241
244
|
- !ruby/object:Gem::Version
|
242
245
|
version: '0'
|
243
246
|
requirements: []
|
244
|
-
rubygems_version: 3.
|
245
|
-
signing_key:
|
247
|
+
rubygems_version: 3.2.24
|
248
|
+
signing_key:
|
246
249
|
specification_version: 4
|
247
250
|
summary: SAML 2.0 Library
|
248
251
|
test_files:
|
@@ -250,6 +253,7 @@ test_files:
|
|
250
253
|
- spec/lib/logout_response_spec.rb
|
251
254
|
- spec/lib/indexed_object_spec.rb
|
252
255
|
- spec/lib/attribute_spec.rb
|
256
|
+
- spec/lib/signable_spec.rb
|
253
257
|
- spec/lib/entity_spec.rb
|
254
258
|
- spec/lib/attribute_consuming_service_spec.rb
|
255
259
|
- spec/lib/key_spec.rb
|
@@ -270,7 +274,9 @@ test_files:
|
|
270
274
|
- spec/fixtures/xml_missigned_assertion.xml
|
271
275
|
- spec/fixtures/certificate.pem
|
272
276
|
- spec/fixtures/noconditions_response.xml
|
277
|
+
- spec/fixtures/response_with_rsa_key_value.xml
|
273
278
|
- spec/fixtures/entities.xml
|
279
|
+
- spec/fixtures/response_assertion_signed_reffed_from_response.xml
|
274
280
|
- spec/fixtures/xml_signature_wrapping_attack_duplicate_ids.xml
|
275
281
|
- spec/fixtures/response_without_keyinfo.xml
|
276
282
|
- spec/fixtures/response_with_signed_assertion_and_encrypted_subject.xml
|