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 +4 -4
- data/lib/saml2/message.rb +0 -16
- data/lib/saml2/response.rb +3 -16
- data/lib/saml2/signable.rb +21 -38
- data/lib/saml2/version.rb +1 -1
- data/spec/lib/response_spec.rb +4 -43
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e68ac85048800a528987268cfb09cf9ed3adf9e501e14927969970eba7b48d53
|
4
|
+
data.tar.gz: 87edd68a1925ef511840fbff7003cce962ab3fc11bd47e3a9a74cb2300e98136
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45163bf19f9acbf26223766b165d40d62ad8f0fac6fadad901254912064c56db830c66e27a2e2d85bcd07c2be84c62156df516ae35c028b60b4b4ea692ae2645
|
7
|
+
data.tar.gz: 8e7d1b05ea37fc9c39024cfc1c2812170ef09fd4ead00b7b6c174cc9fbf8b0f3e8a8f5c4883ee9257bccfcca79482ffef1d07eac320147f69fed00bd60017480
|
data/lib/saml2/message.rb
CHANGED
@@ -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
|
data/lib/saml2/response.rb
CHANGED
@@ -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
|
data/lib/saml2/signable.rb
CHANGED
@@ -34,24 +34,24 @@ module SAML2
|
|
34
34
|
|
35
35
|
# Validate the signature on this object.
|
36
36
|
#
|
37
|
-
#
|
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
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
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(
|
51
|
-
|
52
|
-
|
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
|
-
|
68
|
-
|
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
|
-
|
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
|
-
|
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
|
108
|
-
validate_signature(fingerprint: fingerprint, cert: cert
|
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.
|
data/lib/saml2/version.rb
CHANGED
data/spec/lib/response_spec.rb
CHANGED
@@ -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
|
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
|
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:
|
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-
|
11
|
+
date: 2018-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|