saml2 2.2.12 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 630f03aeb419e5d7ebae94ab4b809bf460be88903fdfdfe981d96dda990fbb5d
4
- data.tar.gz: 2f17e8e081522cf5c7b2e8b5705bf600fd33fbc68e404203f865c442ffeea85c
3
+ metadata.gz: e68ac85048800a528987268cfb09cf9ed3adf9e501e14927969970eba7b48d53
4
+ data.tar.gz: 87edd68a1925ef511840fbff7003cce962ab3fc11bd47e3a9a74cb2300e98136
5
5
  SHA512:
6
- metadata.gz: cad2fd1128ddfa7d8522ca592f8a72b717554ef0d8cddd55b160183e0656df306bebadd0f19e3b131267f8100f1a80ca0956c2c9a78a7eb0e9ad195eb4b72edc
7
- data.tar.gz: d38744fc8b083257306a25b7d3f2e1538313f7791e2667debac3e915499153699ee8af484d6aed2769dc51baffa35e03694759af7e5b32407b77e973b7c66bef
6
+ metadata.gz: 45163bf19f9acbf26223766b165d40d62ad8f0fac6fadad901254912064c56db830c66e27a2e2d85bcd07c2be84c62156df516ae35c028b60b4b4ea692ae2645
7
+ data.tar.gz: 8e7d1b05ea37fc9c39024cfc1c2812170ef09fd4ead00b7b6c174cc9fbf8b0f3e8a8f5c4883ee9257bccfcca79482ffef1d07eac320147f69fed00bd60017480
@@ -119,22 +119,6 @@ module SAML2
119
119
  true
120
120
  end
121
121
 
122
- # (see Signable#validate_signature)
123
- # @param verification_time
124
- # Ignored. The message's {issue_instant} is always used.
125
- def validate_signature(fingerprint: nil,
126
- cert: nil,
127
- verification_time: issue_instant,
128
- allow_expired_certificate: false,
129
- verify_certificate: true)
130
- # verify the signature (certificate's validity) as of the time the message was generated
131
- super(fingerprint: fingerprint,
132
- cert: cert,
133
- verification_time: verification_time,
134
- allow_expired_certificate: allow_expired_certificate,
135
- verify_certificate: verify_certificate)
136
- end
137
-
138
122
  # (see Signable#sign)
139
123
  def sign(x509_certificate, private_key, algorithm_name = :sha256)
140
124
  super
@@ -89,18 +89,11 @@ module SAML2
89
89
  # @param verification_time optional [DateTime]
90
90
  # Validate timestamps (signing certificate validity, issued at, etc.) as of
91
91
  # this point in time.
92
- # @param allow_expired_certificate optional [true, false]
93
- # Allow signing certificate to be expired.
94
- # @param verify_certificate optional [true, false]
95
- # Don't validate the trust chain or validity dates of the signing
96
- # certificate.
97
92
  # @param ignore_audience_condition optional [true, false]
98
93
  # Don't validate any Audience conditions.
99
94
  def validate(service_provider:,
100
95
  identity_provider:,
101
96
  verification_time: nil,
102
- allow_expired_certificate: false,
103
- verify_certificate: true,
104
97
  ignore_audience_condition: false)
105
98
  raise ArgumentError, "service_provider should be an Entity object" unless service_provider.is_a?(Entity)
106
99
  raise ArgumentError, "service_provider should have at least one service_provider role" unless (sp = service_provider.service_providers.first)
@@ -140,9 +133,7 @@ module SAML2
140
133
 
141
134
  if signed?
142
135
  unless (signature_errors = validate_signature(fingerprint: idp.fingerprints,
143
- cert: certificates,
144
- allow_expired_certificate: allow_expired_certificate,
145
- verify_certificate: verify_certificate)).empty?
136
+ cert: certificates)).empty?
146
137
  return errors.concat(signature_errors)
147
138
  end
148
139
  response_signed = true
@@ -153,9 +144,7 @@ module SAML2
153
144
  # this might be nil, if the assertion was encrypted
154
145
  if assertion&.signed?
155
146
  unless (signature_errors = assertion.validate_signature(fingerprint: idp.fingerprints,
156
- cert: certificates,
157
- allow_expired_certificate: allow_expired_certificate,
158
- verify_certificate: verify_certificate)).empty?
147
+ cert: certificates)).empty?
159
148
  return errors.concat(signature_errors)
160
149
  end
161
150
  assertion_signed = true
@@ -211,9 +200,7 @@ module SAML2
211
200
  # check it now
212
201
  if assertion.signed? && !assertion_signed
213
202
  unless (signature_errors = assertion.validate_signature(fingerprint: idp.fingerprints,
214
- cert: certificates,
215
- allow_expired_certificate: allow_expired_certificate,
216
- verify_certificate: verify_certificate)).empty?
203
+ cert: certificates)).empty?
217
204
  return errors.concat(signature_errors)
218
205
  end
219
206
  assertion_signed = true
@@ -34,24 +34,24 @@ module SAML2
34
34
 
35
35
  # Validate the signature on this object.
36
36
  #
37
- # Either +fingerprint+ or +cert+ must be provided.
37
+ # At least one of +key+, +fingerprint+ or +cert+ must be provided.
38
38
  #
39
+ # @param key optional [String, OpenSSL::PKey::PKey]
40
+ # The exact public key to use. +fingerprint+ and +cert+ are ignored.
39
41
  # @param fingerprint optional [Array<String>, String]
40
42
  # SHA1 fingerprints of trusted certificates. If provided, they will be
41
43
  # checked against the {#signing_key} embedded in the {#signature}, and if
42
44
  # a match is found, the certificate embedded in the signature will be
43
45
  # added to the list of certificates used for verifying the signature.
44
46
  # @param cert optional [Array<String>, String]
45
- # @param allow_expired_certificate [Boolean]
46
- # If set to true, verification_time will be adjusted to be just within the
47
- # certificate's expiration time, so that an expired certificate error will
48
- # not be thrown.
47
+ # A single or array of trusted certificates. If provided, they will be
48
+ # check against the {#signing_key} embedded in the {#signature}, and if
49
+ # a match of public keys only is found, that key will be considered trusted
50
+ # and used to verify the signature.
49
51
  # @return [Array<String>] An empty array on success, details of errors on failure.
50
- def validate_signature(fingerprint: nil,
51
- cert: nil,
52
- verification_time: nil,
53
- allow_expired_certificate: false,
54
- verify_certificate: true)
52
+ def validate_signature(key: nil,
53
+ fingerprint: nil,
54
+ cert: nil)
55
55
  return ["not signed"] unless signed?
56
56
 
57
57
  certs = Array(cert)
@@ -59,39 +59,22 @@ module SAML2
59
59
  # see if any given fingerprints match the certificate embedded in the XML;
60
60
  # if so, extract the certificate, and add it to the allowed certificates list
61
61
  Array(fingerprint).each do |fp|
62
- certs << signing_key.certificate if signing_key&.fingerprint == KeyInfo.format_fingerprint(fp)
62
+ certs << signing_key.certificate if signing_key&.fingerprint == SAML2::KeyInfo.format_fingerprint(fp)
63
63
  end
64
64
  certs = certs.uniq
65
- return ["no trusted certificate found"] if certs.empty?
66
65
 
67
- if verify_certificate == false && signing_key&.certificate
68
- key = signing_key.certificate.public_key.to_s
66
+ trusted_keys = certs.map do |cert|
67
+ cert = cert.is_a?(String) ? OpenSSL::X509::Certificate.new(cert) : cert
68
+ cert.public_key.to_s
69
69
  end
70
-
71
- if signing_key
72
- signing_cert = signing_key.certificate
73
- if allow_expired_certificate
74
- verification_time = signing_cert.not_after - 1
75
- end
76
-
77
- # we explicitly trust the signing certificate, but it's not self-signed;
78
- # xmlsec is weird and decides not to trust it in that case, so we skip
79
- # certificate verification by xmlsec, and do it ourselves
80
- if certs.include?(signing_cert) && signing_cert.issuer != signing_cert.subject
81
- verification_time ||= Time.now.utc
82
- return ["certificate has expired"] if verification_time > signing_cert.not_after
83
- return ["certificate is not yet valid"] if verification_time < signing_cert.not_before
84
-
85
- verify_certificate = false
86
- end
70
+ if signing_key&.certificate && trusted_keys.include?(signing_key.certificate.public_key.to_s)
71
+ key ||= signing_key.certificate.public_key.to_s
87
72
  end
88
- certs = nil if key # we're using a key explicitly, ignoring the certs
73
+
74
+ return ["no trusted signing key found"] if key.nil?
89
75
 
90
76
  begin
91
- result = signature.verify_with(key: key,
92
- certs: certs,
93
- verification_time: verification_time,
94
- verify_certificates: verify_certificate)
77
+ result = signature.verify_with(key: key)
95
78
  result ? [] : ["signature is invalid"]
96
79
  rescue XMLSec::VerificationError => e
97
80
  [e.message]
@@ -104,8 +87,8 @@ module SAML2
104
87
  #
105
88
  # @param (see #validate_signature)
106
89
  # @return [Boolean]
107
- def valid_signature?(fingerprint: nil, cert: nil, verification_time: nil)
108
- validate_signature(fingerprint: fingerprint, cert: cert, verification_time: verification_time).empty?
90
+ def valid_signature?(fingerprint: nil, cert: nil)
91
+ validate_signature(fingerprint: fingerprint, cert: cert).empty?
109
92
  end
110
93
 
111
94
  # Sign this object.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SAML2
4
- VERSION = '2.2.12'
4
+ VERSION = '3.0.0'
5
5
  end
@@ -104,8 +104,7 @@ module SAML2
104
104
 
105
105
  # the signature is still valid (we have to set a weird verification time because the response
106
106
  # was signed with an expired signature)
107
- expect(response.validate_signature(fingerprint: 'afe71c28ef740bc87425be13a2263d37971da1f9',
108
- verification_time: Time.parse("2007-07-14 12:01:34Z"))).to eq []
107
+ expect(response.validate_signature(fingerprint: 'afe71c28ef740bc87425be13a2263d37971da1f9')).to eq []
109
108
 
110
109
  # the comment is ignored, but doesn't truncate the nameid
111
110
  expect(response.assertions.first.subject.name_id.id).to eq 'testuser@example.com'
@@ -175,8 +174,7 @@ module SAML2
175
174
 
176
175
  response = Response.parse(fixture("response_tampered_certificate.xml"))
177
176
  sp_entity.valid_response?(response, idp_entity,
178
- verification_time: Time.parse('2015-02-12T22:51:30Z'),
179
- allow_expired_certificate: true)
177
+ verification_time: Time.parse('2015-02-12T22:51:30Z'))
180
178
  expect(response.errors).to eq ["signature is invalid"]
181
179
  end
182
180
 
@@ -193,7 +191,7 @@ module SAML2
193
191
  idp_entity.identity_providers.first.keys << KeyDescriptor.new(fixture("othercertificate.pem"))
194
192
  sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
195
193
  expect(response.errors.length).to eq 1
196
- expect(response.errors.first).to start_with('error occurred during signature verification')
194
+ expect(response.errors.first).to eq("no trusted signing key found")
197
195
  end
198
196
 
199
197
  it "validates signature by fingerprint" do
@@ -211,7 +209,7 @@ module SAML2
211
209
  idp_entity.identity_providers.first.fingerprints << "1c:37:7d:30:c1:83:18:ea:20:8b:dc:d5:35:b6:16:85:17:58:f7:ca"
212
210
 
213
211
  sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
214
- expect(response.errors).to eq ["no trusted certificate found"]
212
+ expect(response.errors).to eq ["no trusted signing key found"]
215
213
  end
216
214
 
217
215
  it "protects against xml signature wrapping attacks targeting nameid" do
@@ -254,43 +252,6 @@ module SAML2
254
252
  "43:0: ERROR: Element '{http://www.w3.org/2000/09/xmldsig#}Signature': This element is not expected."]
255
253
  end
256
254
 
257
- it "errors on expired certificate" do
258
- response = Response.parse(fixture("test6-response.xml"))
259
- idp_entity.entity_id = 'http://simplesamlphp.dev/simplesaml/saml2/idp/metadata.php'
260
- idp_entity.identity_providers.first.keys.clear
261
- idp_entity.identity_providers.first.fingerprints << "afe71c28ef740bc87425be13a2263d37971da1f9"
262
-
263
- sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse("2012-08-03T20:07:15Z"))
264
- expect(response.errors.length).to eq 1
265
- expect(response.errors.first).to match(/certificate has expired/)
266
- end
267
-
268
- it "ignores expired certificate when requested" do
269
- response = Response.parse(fixture("test6-response.xml"))
270
- sp_entity.entity_id = 'http://shard-2.canvas.dev/saml2'
271
- idp_entity.entity_id = 'http://simplesamlphp.dev/simplesaml/saml2/idp/metadata.php'
272
- idp_entity.identity_providers.first.keys.clear
273
- idp_entity.identity_providers.first.fingerprints << "afe71c28ef740bc87425be13a2263d37971da1f9"
274
-
275
- sp_entity.valid_response?(response, idp_entity,
276
- verification_time: Time.parse("2014-09-16T22:15:53Z"),
277
- allow_expired_certificate: true)
278
- expect(response.errors).to eq []
279
- end
280
-
281
- it "ignores invalid certificate when requested" do
282
- response = Response.parse(fixture("test6-response.xml"))
283
- sp_entity.entity_id = 'http://shard-2.canvas.dev/saml2'
284
- idp_entity.entity_id = 'http://simplesamlphp.dev/simplesaml/saml2/idp/metadata.php'
285
- idp_entity.identity_providers.first.keys.clear
286
- idp_entity.identity_providers.first.fingerprints << "afe71c28ef740bc87425be13a2263d37971da1f9"
287
-
288
- sp_entity.valid_response?(response, idp_entity,
289
- verification_time: Time.parse("2014-09-16T22:15:53Z"),
290
- verify_certificate: false)
291
- expect(response.errors).to eq []
292
- end
293
-
294
255
  it "doesn't break the signature by decrypting elements first" do
295
256
  response = Response.parse(fixture("response_with_signed_assertion_and_encrypted_subject.xml"))
296
257
  sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
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: 2.2.12
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-25 00:00:00.000000000 Z
11
+ date: 2018-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri