ruby-saml 0.8.16 → 0.9

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.
Files changed (90) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -6
  4. data/Gemfile +2 -12
  5. data/README.md +363 -35
  6. data/Rakefile +14 -0
  7. data/changelog.md +22 -9
  8. data/lib/onelogin/ruby-saml/attribute_service.rb +34 -0
  9. data/lib/onelogin/ruby-saml/attributes.rb +26 -64
  10. data/lib/onelogin/ruby-saml/authrequest.rb +47 -89
  11. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
  12. data/lib/onelogin/ruby-saml/logoutrequest.rb +34 -93
  13. data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -24
  14. data/lib/onelogin/ruby-saml/metadata.rb +46 -16
  15. data/lib/onelogin/ruby-saml/response.rb +62 -322
  16. data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
  17. data/lib/onelogin/ruby-saml/settings.rb +54 -121
  18. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +26 -61
  19. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +27 -84
  20. data/lib/onelogin/ruby-saml/utils.rb +32 -199
  21. data/lib/onelogin/ruby-saml/version.rb +1 -1
  22. data/lib/ruby-saml.rb +5 -2
  23. data/lib/schemas/{saml20assertion_schema.xsd → saml-schema-assertion-2.0.xsd} +283 -283
  24. data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
  25. data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
  26. data/lib/schemas/saml-schema-metadata-2.0.xsd +339 -0
  27. data/lib/schemas/{saml20protocol_schema.xsd → saml-schema-protocol-2.0.xsd} +302 -302
  28. data/lib/schemas/sstc-metadata-attr.xsd +35 -0
  29. data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
  30. data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
  31. data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
  32. data/lib/schemas/{xenc_schema.xsd → xenc-schema.xsd} +1 -11
  33. data/lib/schemas/xml.xsd +287 -0
  34. data/lib/schemas/{xmldsig_schema.xsd → xmldsig-core-schema.xsd} +0 -9
  35. data/lib/xml_security.rb +83 -235
  36. data/ruby-saml.gemspec +1 -0
  37. data/test/idp_metadata_parser_test.rb +54 -0
  38. data/test/logoutrequest_test.rb +68 -144
  39. data/test/logoutresponse_test.rb +43 -25
  40. data/test/metadata_test.rb +87 -0
  41. data/test/request_test.rb +103 -90
  42. data/test/response_test.rb +181 -471
  43. data/test/responses/idp_descriptor.xml +3 -0
  44. data/test/responses/logoutresponse_fixtures.rb +5 -5
  45. data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
  46. data/test/responses/response_with_multiple_attribute_values.xml +1 -1
  47. data/test/responses/slo_request.xml +4 -0
  48. data/test/settings_test.rb +25 -112
  49. data/test/slo_logoutrequest_test.rb +41 -44
  50. data/test/slo_logoutresponse_test.rb +87 -167
  51. data/test/test_helper.rb +27 -102
  52. data/test/xml_security_test.rb +114 -337
  53. metadata +34 -84
  54. data/lib/onelogin/ruby-saml/setting_error.rb +0 -6
  55. data/test/certificates/certificate.der +0 -0
  56. data/test/certificates/formatted_certificate +0 -14
  57. data/test/certificates/formatted_chained_certificate +0 -42
  58. data/test/certificates/formatted_private_key +0 -12
  59. data/test/certificates/formatted_rsa_private_key +0 -12
  60. data/test/certificates/invalid_certificate1 +0 -1
  61. data/test/certificates/invalid_certificate2 +0 -1
  62. data/test/certificates/invalid_certificate3 +0 -12
  63. data/test/certificates/invalid_chained_certificate1 +0 -1
  64. data/test/certificates/invalid_private_key1 +0 -1
  65. data/test/certificates/invalid_private_key2 +0 -1
  66. data/test/certificates/invalid_private_key3 +0 -10
  67. data/test/certificates/invalid_rsa_private_key1 +0 -1
  68. data/test/certificates/invalid_rsa_private_key2 +0 -1
  69. data/test/certificates/invalid_rsa_private_key3 +0 -10
  70. data/test/certificates/ruby-saml-2.crt +0 -15
  71. data/test/requests/logoutrequest_fixtures.rb +0 -47
  72. data/test/responses/encrypted_new_attack.xml.base64 +0 -1
  73. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  74. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  75. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  76. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  77. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  78. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  79. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  80. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  81. data/test/responses/response_with_concealed_signed_assertion.xml +0 -51
  82. data/test/responses/response_with_doubled_signed_assertion.xml +0 -49
  83. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  84. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  85. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  86. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  87. data/test/responses/response_wrapped.xml.base64 +0 -150
  88. data/test/responses/valid_response.xml.base64 +0 -1
  89. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  90. 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#
@@ -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/TR/2001/REC-xml-c14n-20010315",
53
- "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
54
- Nokogiri::XML::XML_C14N_1_0
55
- when "http://www.w3.org/2006/12/xml-c14n11",
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
- RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
84
- RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
85
- RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
86
- RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
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 md"
79
+ INC_PREFIX_LIST = "#default samlp saml ds xs xsi"
93
80
 
94
- attr_writer :uuid
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 = RSA_SHA1, digest_method = SHA1)
118
- noko = Nokogiri::XML(self.to_s) do |config|
119
- config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
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" => signature_method})
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
- c14element = transforms_element.add_element("ds:Transform", {"Algorithm" => C14N})
134
- c14element.add_element("ec:InclusiveNamespaces", {"xmlns:ec" => C14N, "PrefixList" => INC_PREFIX_LIST})
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::XML(signature_element.to_s) do |config|
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
- if sp_sso_descriptor = self.elements["/md:EntityDescriptor"]
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
- attr_writer :signed_element_id
159
+ attr_accessor :signed_element_id
160
+ attr_accessor :errors
190
161
 
191
- def signed_element_id
192
- @signed_element_id ||= extract_signed_element_id
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, options = {})
168
+ def validate_document(idp_cert_fingerprint, soft = true)
196
169
  # get cert from response
197
- cert_element = REXML::XPath.first(
198
- self,
199
- "//ds:X509Certificate",
200
- { "ds"=>DSIG }
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
- return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate) and not cert provided at settings"))
175
+ raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate)")
232
176
  end
233
177
  end
234
- validate_signature(base64_cert, soft)
235
- end
178
+ base64_cert = cert_element.text
179
+ cert_text = Base64.decode64(base64_cert)
180
+ cert = OpenSSL::X509::Certificate.new(cert_text)
236
181
 
237
- def validate_document_with_cert(idp_cert, soft = true)
238
- # get cert from response
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
- # check saml response cert matches provided idp cert
255
- if idp_cert.to_pem != cert.to_pem
256
- return false
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
- validate_signature(base64_cert, true)
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
- document = Nokogiri::XML(self.to_s) do |config|
269
- config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
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
- # get signature node
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
- if sig_element.nil?
283
- return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("No Signature Node"))
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 method
287
- sig_alg_value = REXML::XPath.first(
288
- sig_element,
289
- "./ds:SignedInfo/ds:SignatureMethod",
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
- signature = Base64.decode64(OneLogin::RubySaml::Utils.element_text(base64_signature))
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
- ref = REXML::XPath.first(sig_element, "//ds:Reference", {"ds"=>DSIG})
325
-
326
- hashed_element = document.at_xpath("//*[@ID=$id]", nil, { 'id' => extract_signed_element_id })
327
-
328
- canon_algorithm = canon_algorithm REXML::XPath.first(
329
- ref,
330
- '//ds:CanonicalizationMethod',
331
- { "ds" => DSIG }
332
- )
333
-
334
- canon_algorithm = process_transforms(ref, canon_algorithm)
335
-
336
- canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
337
-
338
- digest_algorithm = algorithm(REXML::XPath.first(
339
- ref,
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 = Base64.decode64(base64_cert)
357
- cert = OpenSSL::X509::Certificate.new(cert_text)
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 = REXML::XPath.first(
402
- self,
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
- nil
271
+ []
424
272
  end
425
273
  end
426
274