ruby-saml 0.8.16 → 0.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +1 -6
- data/Gemfile +2 -12
- data/README.md +363 -35
- data/Rakefile +14 -0
- data/changelog.md +22 -9
- data/lib/onelogin/ruby-saml/attribute_service.rb +34 -0
- data/lib/onelogin/ruby-saml/attributes.rb +26 -64
- data/lib/onelogin/ruby-saml/authrequest.rb +47 -89
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +34 -93
- data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -24
- data/lib/onelogin/ruby-saml/metadata.rb +46 -16
- data/lib/onelogin/ruby-saml/response.rb +62 -322
- data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
- data/lib/onelogin/ruby-saml/settings.rb +54 -121
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +26 -61
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +27 -84
- data/lib/onelogin/ruby-saml/utils.rb +32 -199
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/ruby-saml.rb +5 -2
- data/lib/schemas/{saml20assertion_schema.xsd → saml-schema-assertion-2.0.xsd} +283 -283
- data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
- data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
- data/lib/schemas/saml-schema-metadata-2.0.xsd +339 -0
- data/lib/schemas/{saml20protocol_schema.xsd → saml-schema-protocol-2.0.xsd} +302 -302
- data/lib/schemas/sstc-metadata-attr.xsd +35 -0
- data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
- data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
- data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
- data/lib/schemas/{xenc_schema.xsd → xenc-schema.xsd} +1 -11
- data/lib/schemas/xml.xsd +287 -0
- data/lib/schemas/{xmldsig_schema.xsd → xmldsig-core-schema.xsd} +0 -9
- data/lib/xml_security.rb +83 -235
- data/ruby-saml.gemspec +1 -0
- data/test/idp_metadata_parser_test.rb +54 -0
- data/test/logoutrequest_test.rb +68 -144
- data/test/logoutresponse_test.rb +43 -25
- data/test/metadata_test.rb +87 -0
- data/test/request_test.rb +103 -90
- data/test/response_test.rb +181 -471
- data/test/responses/idp_descriptor.xml +3 -0
- data/test/responses/logoutresponse_fixtures.rb +5 -5
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
- data/test/responses/response_with_multiple_attribute_values.xml +1 -1
- data/test/responses/slo_request.xml +4 -0
- data/test/settings_test.rb +25 -112
- data/test/slo_logoutrequest_test.rb +41 -44
- data/test/slo_logoutresponse_test.rb +87 -167
- data/test/test_helper.rb +27 -102
- data/test/xml_security_test.rb +114 -337
- metadata +34 -84
- data/lib/onelogin/ruby-saml/setting_error.rb +0 -6
- data/test/certificates/certificate.der +0 -0
- data/test/certificates/formatted_certificate +0 -14
- data/test/certificates/formatted_chained_certificate +0 -42
- data/test/certificates/formatted_private_key +0 -12
- data/test/certificates/formatted_rsa_private_key +0 -12
- data/test/certificates/invalid_certificate1 +0 -1
- data/test/certificates/invalid_certificate2 +0 -1
- data/test/certificates/invalid_certificate3 +0 -12
- data/test/certificates/invalid_chained_certificate1 +0 -1
- data/test/certificates/invalid_private_key1 +0 -1
- data/test/certificates/invalid_private_key2 +0 -1
- data/test/certificates/invalid_private_key3 +0 -10
- data/test/certificates/invalid_rsa_private_key1 +0 -1
- data/test/certificates/invalid_rsa_private_key2 +0 -1
- data/test/certificates/invalid_rsa_private_key3 +0 -10
- data/test/certificates/ruby-saml-2.crt +0 -15
- data/test/requests/logoutrequest_fixtures.rb +0 -47
- data/test/responses/encrypted_new_attack.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
- data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
- data/test/responses/invalids/no_signature.xml.base64 +0 -1
- data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
- data/test/responses/response_node_text_attack.xml.base64 +0 -1
- data/test/responses/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
- data/test/responses/response_with_signed_assertion_3.xml +0 -30
- data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
- data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
- data/test/responses/response_wrapped.xml.base64 +0 -150
- data/test/responses/valid_response.xml.base64 +0 -1
- data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
- data/test/utils_test.rb +0 -231
@@ -1,13 +1,4 @@
|
|
1
1
|
<?xml version="1.0" encoding="utf-8"?>
|
2
|
-
<!DOCTYPE schema
|
3
|
-
PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd"
|
4
|
-
[
|
5
|
-
<!ATTLIST schema
|
6
|
-
xmlns:ds CDATA #FIXED "http://www.w3.org/2000/09/xmldsig#">
|
7
|
-
<!ENTITY dsig 'http://www.w3.org/2000/09/xmldsig#'>
|
8
|
-
<!ENTITY % p ''>
|
9
|
-
<!ENTITY % s ''>
|
10
|
-
]>
|
11
2
|
|
12
3
|
<!-- Schema for XML Signatures
|
13
4
|
http://www.w3.org/2000/09/xmldsig#
|
data/lib/xml_security.rb
CHANGED
@@ -30,17 +30,13 @@ require 'nokogiri'
|
|
30
30
|
require "digest/sha1"
|
31
31
|
require "digest/sha2"
|
32
32
|
require "onelogin/ruby-saml/validation_error"
|
33
|
-
require "onelogin/ruby-saml/utils"
|
34
33
|
|
35
34
|
module XMLSecurity
|
36
35
|
|
37
36
|
class BaseDocument < REXML::Document
|
38
|
-
REXML::Document::entity_expansion_limit = 0
|
39
37
|
|
40
38
|
C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
|
41
39
|
DSIG = "http://www.w3.org/2000/09/xmldsig#"
|
42
|
-
NOKOGIRI_OPTIONS = Nokogiri::XML::ParseOptions::STRICT |
|
43
|
-
Nokogiri::XML::ParseOptions::NONET
|
44
40
|
|
45
41
|
def canon_algorithm(element)
|
46
42
|
algorithm = element
|
@@ -49,14 +45,10 @@ module XMLSecurity
|
|
49
45
|
end
|
50
46
|
|
51
47
|
case algorithm
|
52
|
-
when "http://www.w3.org/
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
"http://www.w3.org/2006/12/xml-c14n11#WithComments"
|
57
|
-
Nokogiri::XML::XML_C14N_1_1
|
58
|
-
else
|
59
|
-
Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
48
|
+
when "http://www.w3.org/2001/10/xml-exc-c14n#" then Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
49
|
+
when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" then Nokogiri::XML::XML_C14N_1_0
|
50
|
+
when "http://www.w3.org/2006/12/xml-c14n11" then Nokogiri::XML::XML_C14N_1_1
|
51
|
+
else Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
60
52
|
end
|
61
53
|
end
|
62
54
|
|
@@ -64,10 +56,9 @@ module XMLSecurity
|
|
64
56
|
algorithm = element
|
65
57
|
if algorithm.is_a?(REXML::Element)
|
66
58
|
algorithm = element.attribute("Algorithm").value
|
59
|
+
algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
|
67
60
|
end
|
68
61
|
|
69
|
-
algorithm = algorithm && algorithm =~ /(rsa-)?sha(.*?)$/i && $2.to_i
|
70
|
-
|
71
62
|
case algorithm
|
72
63
|
when 256 then OpenSSL::Digest::SHA256
|
73
64
|
when 384 then OpenSSL::Digest::SHA384
|
@@ -80,24 +71,14 @@ module XMLSecurity
|
|
80
71
|
end
|
81
72
|
|
82
73
|
class Document < BaseDocument
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
|
88
|
-
SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256'
|
89
|
-
SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"
|
90
|
-
SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512'
|
74
|
+
SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
|
75
|
+
SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
|
76
|
+
SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
|
77
|
+
SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
|
91
78
|
ENVELOPED_SIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
|
92
|
-
INC_PREFIX_LIST = "#default samlp saml ds xs xsi
|
79
|
+
INC_PREFIX_LIST = "#default samlp saml ds xs xsi"
|
93
80
|
|
94
|
-
|
95
|
-
|
96
|
-
def uuid
|
97
|
-
@uuid ||= begin
|
98
|
-
document.root.nil? ? nil : document.root.attributes['ID']
|
99
|
-
end
|
100
|
-
end
|
81
|
+
attr_accessor :uuid
|
101
82
|
|
102
83
|
#<Signature>
|
103
84
|
#<SignedInfo>
|
@@ -114,15 +95,14 @@ module XMLSecurity
|
|
114
95
|
#<KeyInfo />
|
115
96
|
#<Object />
|
116
97
|
#</Signature>
|
117
|
-
def sign_document(private_key, certificate, signature_method =
|
118
|
-
noko = Nokogiri
|
119
|
-
|
120
|
-
end
|
98
|
+
def sign_document(private_key, certificate, signature_method = SHA1, digest_method = SHA1)
|
99
|
+
noko = Nokogiri.parse(self.to_s)
|
100
|
+
canon_doc = noko.canonicalize(canon_algorithm(C14N))
|
121
101
|
|
122
102
|
signature_element = REXML::Element.new("ds:Signature").add_namespace('ds', DSIG)
|
123
103
|
signed_info_element = signature_element.add_element("ds:SignedInfo")
|
124
104
|
signed_info_element.add_element("ds:CanonicalizationMethod", {"Algorithm" => C14N})
|
125
|
-
signed_info_element.add_element("ds:SignatureMethod", {"Algorithm"
|
105
|
+
signed_info_element.add_element("ds:SignatureMethod", {"Algorithm"=>signature_method})
|
126
106
|
|
127
107
|
# Add Reference
|
128
108
|
reference_element = signed_info_element.add_element("ds:Reference", {"URI" => "##{uuid}"})
|
@@ -130,22 +110,16 @@ module XMLSecurity
|
|
130
110
|
# Add Transforms
|
131
111
|
transforms_element = reference_element.add_element("ds:Transforms")
|
132
112
|
transforms_element.add_element("ds:Transform", {"Algorithm" => ENVELOPED_SIG})
|
133
|
-
|
134
|
-
|
113
|
+
transforms_element.add_element("ds:Transform", {"Algorithm" => C14N})
|
114
|
+
transforms_element.add_element("ds:InclusiveNamespaces", {"xmlns" => C14N, "PrefixList" => INC_PREFIX_LIST})
|
135
115
|
|
136
116
|
digest_method_element = reference_element.add_element("ds:DigestMethod", {"Algorithm" => digest_method})
|
137
|
-
inclusive_namespaces = INC_PREFIX_LIST.split(" ")
|
138
|
-
canon_doc = noko.canonicalize(canon_algorithm(C14N), inclusive_namespaces)
|
139
117
|
reference_element.add_element("ds:DigestValue").text = compute_digest(canon_doc, algorithm(digest_method_element))
|
140
118
|
|
141
119
|
# add SignatureValue
|
142
|
-
noko_sig_element = Nokogiri
|
143
|
-
config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
|
144
|
-
end
|
145
|
-
|
120
|
+
noko_sig_element = Nokogiri.parse(signature_element.to_s)
|
146
121
|
noko_signed_info_element = noko_sig_element.at_xpath('//ds:Signature/ds:SignedInfo', 'ds' => DSIG)
|
147
122
|
canon_string = noko_signed_info_element.canonicalize(canon_algorithm(C14N))
|
148
|
-
|
149
123
|
signature = compute_signature(private_key, algorithm(signature_method).new, canon_string)
|
150
124
|
signature_element.add_element("ds:SignatureValue").text = signature
|
151
125
|
|
@@ -163,11 +137,7 @@ module XMLSecurity
|
|
163
137
|
if issuer_element
|
164
138
|
self.root.insert_after issuer_element, signature_element
|
165
139
|
else
|
166
|
-
|
167
|
-
self.root.insert_before sp_sso_descriptor, signature_element
|
168
|
-
else
|
169
|
-
self.root.add_element(signature_element)
|
170
|
-
end
|
140
|
+
self.root.add_element(signature_element)
|
171
141
|
end
|
172
142
|
end
|
173
143
|
|
@@ -186,178 +156,96 @@ module XMLSecurity
|
|
186
156
|
|
187
157
|
class SignedDocument < BaseDocument
|
188
158
|
|
189
|
-
|
159
|
+
attr_accessor :signed_element_id
|
160
|
+
attr_accessor :errors
|
190
161
|
|
191
|
-
def
|
192
|
-
|
162
|
+
def initialize(response, errors = [])
|
163
|
+
super(response)
|
164
|
+
@errors = errors
|
165
|
+
extract_signed_element_id
|
193
166
|
end
|
194
167
|
|
195
|
-
def validate_document(idp_cert_fingerprint, soft = true
|
168
|
+
def validate_document(idp_cert_fingerprint, soft = true)
|
196
169
|
# get cert from response
|
197
|
-
cert_element = REXML::XPath.first(
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
)
|
202
|
-
|
203
|
-
if cert_element
|
204
|
-
base64_cert = OneLogin::RubySaml::Utils.element_text(cert_element)
|
205
|
-
cert_text = Base64.decode64(base64_cert)
|
206
|
-
begin
|
207
|
-
cert = OpenSSL::X509::Certificate.new(cert_text)
|
208
|
-
rescue OpenSSL::X509::CertificateError => _e
|
209
|
-
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Certificate Error"))
|
210
|
-
end
|
211
|
-
|
212
|
-
if options[:fingerprint_alg]
|
213
|
-
fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new
|
214
|
-
else
|
215
|
-
fingerprint_alg = OpenSSL::Digest::SHA1.new
|
216
|
-
end
|
217
|
-
fingerprint = fingerprint_alg.hexdigest(cert.to_der)
|
218
|
-
|
219
|
-
# check cert matches registered idp cert
|
220
|
-
if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
|
221
|
-
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch"))
|
222
|
-
end
|
223
|
-
else
|
224
|
-
if options[:cert]
|
225
|
-
cert = options[:cert]
|
226
|
-
if cert.is_a? String
|
227
|
-
cert = OpenSSL::X509::Certificate.new(cert)
|
228
|
-
end
|
229
|
-
base64_cert = Base64.encode64(cert.to_pem)
|
170
|
+
cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG })
|
171
|
+
unless cert_element
|
172
|
+
if soft
|
173
|
+
return false
|
230
174
|
else
|
231
|
-
|
175
|
+
raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate)")
|
232
176
|
end
|
233
177
|
end
|
234
|
-
|
235
|
-
|
178
|
+
base64_cert = cert_element.text
|
179
|
+
cert_text = Base64.decode64(base64_cert)
|
180
|
+
cert = OpenSSL::X509::Certificate.new(cert_text)
|
236
181
|
|
237
|
-
|
238
|
-
|
239
|
-
cert_element = REXML::XPath.first(
|
240
|
-
self,
|
241
|
-
"//ds:X509Certificate",
|
242
|
-
{ "ds"=>DSIG }
|
243
|
-
)
|
244
|
-
|
245
|
-
if cert_element
|
246
|
-
base64_cert = OneLogin::RubySaml::Utils.element_text(cert_element)
|
247
|
-
cert_text = Base64.decode64(base64_cert)
|
248
|
-
begin
|
249
|
-
cert = OpenSSL::X509::Certificate.new(cert_text)
|
250
|
-
rescue OpenSSL::X509::CertificateError => _e
|
251
|
-
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Certificate Error"))
|
252
|
-
end
|
182
|
+
# check cert matches registered idp cert
|
183
|
+
fingerprint = Digest::SHA1.hexdigest(cert.to_der)
|
253
184
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
end
|
258
|
-
elsif not idp_cert
|
259
|
-
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate) and not cert provided at settings"))
|
260
|
-
else
|
261
|
-
base64_cert = Base64.encode64(idp_cert.to_pem)
|
185
|
+
if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
|
186
|
+
@errors << "Fingerprint mismatch"
|
187
|
+
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch"))
|
262
188
|
end
|
263
|
-
|
189
|
+
|
190
|
+
validate_signature(base64_cert, soft)
|
264
191
|
end
|
265
192
|
|
266
193
|
def validate_signature(base64_cert, soft = true)
|
194
|
+
# validate references
|
267
195
|
|
268
|
-
|
269
|
-
|
270
|
-
end
|
271
|
-
|
272
|
-
# create a rexml document
|
273
|
-
@working_copy ||= REXML::Document.new(self.to_s).root
|
196
|
+
# check for inclusive namespaces
|
197
|
+
inclusive_namespaces = extract_inclusive_namespaces
|
274
198
|
|
275
|
-
|
276
|
-
sig_element = REXML::XPath.first(
|
277
|
-
@working_copy,
|
278
|
-
"//ds:Signature",
|
279
|
-
{"ds"=>DSIG}
|
280
|
-
)
|
199
|
+
document = Nokogiri.parse(self.to_s)
|
281
200
|
|
282
|
-
|
283
|
-
|
284
|
-
end
|
201
|
+
# create a working copy so we don't modify the original
|
202
|
+
@working_copy ||= REXML::Document.new(self.to_s).root
|
285
203
|
|
286
|
-
# signature
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
{"ds"=>DSIG}
|
291
|
-
)
|
292
|
-
signature_algorithm = algorithm(sig_alg_value)
|
293
|
-
|
294
|
-
# get signature
|
295
|
-
base64_signature = REXML::XPath.first(
|
296
|
-
sig_element,
|
297
|
-
"./ds:SignatureValue",
|
298
|
-
{"ds" => DSIG}
|
299
|
-
)
|
300
|
-
|
301
|
-
if base64_signature.nil?
|
302
|
-
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("SignatureValue not found"))
|
204
|
+
# store and remove signature node
|
205
|
+
@sig_element ||= begin
|
206
|
+
element = REXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG})
|
207
|
+
element.remove
|
303
208
|
end
|
304
209
|
|
305
|
-
|
306
|
-
|
307
|
-
# canonicalization method
|
308
|
-
canon_algorithm = canon_algorithm REXML::XPath.first(
|
309
|
-
sig_element,
|
310
|
-
'./ds:SignedInfo/ds:CanonicalizationMethod',
|
311
|
-
'ds' => DSIG
|
312
|
-
)
|
313
|
-
|
210
|
+
# verify signature
|
211
|
+
signed_info_element = REXML::XPath.first(@sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
|
314
212
|
noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
|
315
213
|
noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
|
316
|
-
|
214
|
+
canon_algorithm = canon_algorithm REXML::XPath.first(@sig_element, '//ds:CanonicalizationMethod', 'ds' => DSIG)
|
317
215
|
canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
|
318
216
|
noko_sig_element.remove
|
319
217
|
|
320
|
-
# get inclusive namespaces
|
321
|
-
inclusive_namespaces = extract_inclusive_namespaces
|
322
|
-
|
323
218
|
# check digests
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
"//ds:DigestMethod",
|
341
|
-
{ "ds" => DSIG }
|
342
|
-
))
|
343
|
-
hash = digest_algorithm.digest(canon_hashed_element)
|
344
|
-
encoded_digest_value = REXML::XPath.first(
|
345
|
-
ref,
|
346
|
-
"//ds:DigestValue",
|
347
|
-
{ "ds" => DSIG }
|
348
|
-
)
|
349
|
-
digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(encoded_digest_value))
|
350
|
-
|
351
|
-
unless digests_match?(hash, digest_value)
|
352
|
-
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Digest mismatch"))
|
219
|
+
REXML::XPath.each(@sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
|
220
|
+
uri = ref.attributes.get_attribute("URI").value
|
221
|
+
|
222
|
+
hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']")
|
223
|
+
canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG)
|
224
|
+
canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
|
225
|
+
|
226
|
+
digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod", 'ds' => DSIG))
|
227
|
+
|
228
|
+
hash = digest_algorithm.digest(canon_hashed_element)
|
229
|
+
digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text)
|
230
|
+
|
231
|
+
unless digests_match?(hash, digest_value)
|
232
|
+
@errors << "Digest mismatch"
|
233
|
+
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Digest mismatch"))
|
234
|
+
end
|
353
235
|
end
|
354
236
|
|
237
|
+
base64_signature = REXML::XPath.first(@sig_element, "//ds:SignatureValue", {"ds"=>DSIG}).text
|
238
|
+
signature = Base64.decode64(base64_signature)
|
239
|
+
|
355
240
|
# get certificate object
|
356
|
-
cert_text
|
357
|
-
cert
|
241
|
+
cert_text = Base64.decode64(base64_cert)
|
242
|
+
cert = OpenSSL::X509::Certificate.new(cert_text)
|
243
|
+
|
244
|
+
# signature method
|
245
|
+
signature_algorithm = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}))
|
358
246
|
|
359
|
-
# verify signature
|
360
247
|
unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
|
248
|
+
@errors << "Key validation error"
|
361
249
|
return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Key validation error"))
|
362
250
|
end
|
363
251
|
|
@@ -366,61 +254,21 @@ module XMLSecurity
|
|
366
254
|
|
367
255
|
private
|
368
256
|
|
369
|
-
def process_transforms(ref, canon_algorithm)
|
370
|
-
transforms = REXML::XPath.match(
|
371
|
-
ref,
|
372
|
-
"//ds:Transforms/ds:Transform",
|
373
|
-
{ "ds" => DSIG }
|
374
|
-
)
|
375
|
-
|
376
|
-
transforms.each do |transform_element|
|
377
|
-
if transform_element.attributes && transform_element.attributes["Algorithm"]
|
378
|
-
algorithm = transform_element.attributes["Algorithm"]
|
379
|
-
case algorithm
|
380
|
-
when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
|
381
|
-
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
|
382
|
-
canon_algorithm = Nokogiri::XML::XML_C14N_1_0
|
383
|
-
when "http://www.w3.org/2006/12/xml-c14n11",
|
384
|
-
"http://www.w3.org/2006/12/xml-c14n11#WithComments"
|
385
|
-
canon_algorithm = Nokogiri::XML::XML_C14N_1_1
|
386
|
-
when "http://www.w3.org/2001/10/xml-exc-c14n#",
|
387
|
-
"http://www.w3.org/2001/10/xml-exc-c14n#WithComments"
|
388
|
-
canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
389
|
-
end
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
canon_algorithm
|
394
|
-
end
|
395
|
-
|
396
257
|
def digests_match?(hash, digest_value)
|
397
258
|
hash == digest_value
|
398
259
|
end
|
399
260
|
|
400
261
|
def extract_signed_element_id
|
401
|
-
reference_element
|
402
|
-
|
403
|
-
"//ds:Signature/ds:SignedInfo/ds:Reference",
|
404
|
-
{"ds"=>DSIG}
|
405
|
-
)
|
406
|
-
|
407
|
-
return nil if reference_element.nil?
|
408
|
-
|
409
|
-
sei = reference_element.attribute("URI").value[1..-1]
|
410
|
-
sei.nil? ? reference_element.parent.parent.parent.attribute("ID").value : sei
|
262
|
+
reference_element = REXML::XPath.first(self, "//ds:Signature/ds:SignedInfo/ds:Reference", {"ds"=>DSIG})
|
263
|
+
self.signed_element_id = reference_element.attribute("URI").value[1..-1] unless reference_element.nil?
|
411
264
|
end
|
412
265
|
|
413
266
|
def extract_inclusive_namespaces
|
414
|
-
element = REXML::XPath.first(
|
415
|
-
self,
|
416
|
-
"//ec:InclusiveNamespaces",
|
417
|
-
{ "ec" => C14N }
|
418
|
-
)
|
419
|
-
if element
|
267
|
+
if element = REXML::XPath.first(self, "//ec:InclusiveNamespaces", { "ec" => C14N })
|
420
268
|
prefix_list = element.attributes.get_attribute("PrefixList").value
|
421
269
|
prefix_list.split(" ")
|
422
270
|
else
|
423
|
-
|
271
|
+
[]
|
424
272
|
end
|
425
273
|
end
|
426
274
|
|