saml2 3.0.8 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1dff92fc9d0d5ebac2fdef1ec37c5359a0d7bbde83d0d6faa0a8dbacedcf538a
4
- data.tar.gz: 3222ef01c4bb421bf27df8e308b64a03fc8c31749283f3ade35d64d425667bcf
3
+ metadata.gz: 1a9f01162da7309af02b28de55593de014c32eba85d87a7af95360423cd03550
4
+ data.tar.gz: 71b3e1f1309c840e649e8d6a3dfad701b01349c5c8b3c3378b96733ef62718cf
5
5
  SHA512:
6
- metadata.gz: 1e4dd06e08cc35fb4e7b6a519d66c097bb0c94fab0c8a6a6bc5a417772c737ea03c577f68f69ce49a271e091b48dad7efb9f664f05c11b8a7b961ad965b8a83a
7
- data.tar.gz: 44edac0a66662fc20cde06931927bed8f308d0842e03de1223bb4c73d7defbb016dbd6f55b37c1f2a5de1e8c0800413db4f7175c77a1b7a80b7763ee8557b831
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:KeyInfo/dsig:X509Data/dsig:X509Certificate', Namespaces::ALL)&.content&.strip
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
- key_info['dsig'].X509Data do |x509_data|
51
- x509_data['dsig'].X509Certificate(x509)
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
@@ -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
- if idp.fingerprints.empty? && certificates.empty?
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(fingerprint: idp.fingerprints,
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(fingerprint: idp.fingerprints,
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
@@ -20,6 +20,8 @@ module SAML2
20
20
  # (see Base#from_xml)
21
21
  def from_xml(node)
22
22
  super
23
+ remove_instance_variable(:@authn_requests_signed)
24
+ remove_instance_variable(:@want_assertions_signed)
23
25
  @assertion_consumer_services = nil
24
26
  @attribute_consuming_services = nil
25
27
  end
@@ -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.at_xpath('dsig:Signature', Namespaces::ALL)
11
- if @signature
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
- @signature = nil unless xml == xml.document.root
13
+ true if xml == xml.document.root
15
14
  elsif signed_node != "##{xml['ID']}"
16
- @signature = nil
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(signature) : nil
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
- # The exact public key to use. +fingerprint+ and +cert+ are ignored.
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 certificate embedded in the signature will be
49
- # added to the list of certificates used for verifying the signature.
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
- # check against the {#signing_key} embedded in the {#signature}, and if
53
- # a match of public keys only is found, that key will be considered trusted
54
- # and used to verify the signature.
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 = certs.map do |cert|
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
- if signing_key&.certificate && trusted_keys.include?(signing_key.certificate.public_key.to_s)
75
- key ||= signing_key.certificate.public_key.to_s
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? && key.nil? && trusted_keys.length == 1
80
- key = trusted_keys.first
82
+ if signing_key.nil?
83
+ verification_key = trusted_keys.first
81
84
  end
82
85
 
83
- return ["no trusted signing key found"] if key.nil?
86
+ return ["no trusted signing key found"] if verification_key.nil?
84
87
 
85
88
  begin
86
- result = signature.verify_with(key: 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?(fingerprint: nil, cert: nil)
100
- validate_signature(fingerprint: fingerprint, cert: cert).empty?
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,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SAML2
4
- VERSION = '3.0.8'
4
+ VERSION = '3.1.0'
5
5
  end
@@ -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#">
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
@@ -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(url, public_key: certificate) }.to raise_error(UnsupportedSignatureAlgorithm)
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
 
3
5
  module SAML2
@@ -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
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'byebug'
1
4
  require 'saml2'
2
5
 
3
6
  def fixture(name)
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.8
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: 2019-07-30 00:00:00.000000000 Z
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.11'
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.11'
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.1'
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.1'
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: '10.0'
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: '10.0'
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.0.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