[DRE-commits] [ruby-saml] 01/07: Imported Upstream version 1.1.2

Praveen Arimbrathodiyil praveen at moszumanska.debian.org
Tue Mar 15 19:11:55 UTC 2016


This is an automated email from the git hooks/post-receive script.

praveen pushed a commit to branch master
in repository ruby-saml.

commit 89ee9a86739ea3618142c442d7554ebba2b0c463
Author: Praveen Arimbrathodiyil <praveen at debian.org>
Date:   Tue Mar 15 22:48:16 2016 +0530

    Imported Upstream version 1.1.2
---
 .travis.yml                                        |   7 +
 README.md                                          |  26 +-
 Rakefile                                           |  14 -
 changelog.md                                       |  20 +
 lib/onelogin/ruby-saml/authrequest.rb              |  13 +-
 lib/onelogin/ruby-saml/idp_metadata_parser.rb      |  22 +-
 lib/onelogin/ruby-saml/response.rb                 | 103 +++--
 lib/onelogin/ruby-saml/saml_message.rb             |   2 +-
 lib/onelogin/ruby-saml/settings.rb                 |   8 +-
 lib/onelogin/ruby-saml/utils.rb                    |   2 +-
 lib/onelogin/ruby-saml/version.rb                  |   2 +-
 lib/xml_security.rb                                | 173 +++++----
 metadata.yml                                       | 414 ---------------------
 ruby-saml.gemspec                                  |  14 +-
 test/response_test.rb                              | 153 +++++++-
 .../invalids/signature_wrapping_attack.xml.base64  |   1 +
 ...sponse_with_ds_namespace_at_the_root.xml.base64 |   1 +
 .../response_with_signed_message_and_assertion.xml |  34 ++
 .../response_without_reference_uri.xml.base64      |   1 +
 test/responses/signed_nameid_in_atts.xml           |  47 +++
 test/responses/signed_unqual_nameid_in_atts.xml    |  47 +++
 ...lid_response_without_x509certificate.xml.base64 |   1 +
 test/test_helper.rb                                |  13 +
 test/xml_security_test.rb                          |  42 ++-
 24 files changed, 587 insertions(+), 573 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 5c7a02f..c7ad410 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,4 @@
+sudo: false
 language: ruby
 rvm:
   - 1.8.7
@@ -6,6 +7,8 @@ rvm:
   - 2.1.5
   - 2.2.0
   - ree
+  - jruby-1.7.21
+  - jruby-9.0.0.0
 gemfile:
   - Gemfile
   - gemfiles/nokogiri-1.5.gemfile
@@ -15,3 +18,7 @@ matrix:
       gemfile: Gemfile
     - rvm: ree
       gemfile: Gemfile
+    - rvm: jruby-9.0.0.0
+      gemfile: gemfiles/nokogiri-1.5.gemfile
+    - rvm: jruby-1.7.21
+      gemfile: gemfiles/nokogiri-1.5.gemfile
diff --git a/README.md b/README.md
index a51496c..64d4ecb 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,18 @@
-# Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.png)](http://travis-ci.org/onelogin/ruby-saml)
+# Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.png)](http://travis-ci.org/onelogin/ruby-saml) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master%0A)](https://coveralls.io/r/onelogin/ruby-saml?branch=master%0A) [![Gem Version](https://badge.fury.io/rb/ruby-saml.svg)](http://badge.fury.io/rb/ruby-saml)
 
 
+## Updating from 1.0.x to 1.1.X
+
+Version `1.1` adds some improvements on signature validation and solves some namespace conflicts.
+
+For more details, please review [the changelog](changelog.md).
+
 ## Updating from 0.9.x to 1.0.X
 
 Version `1.0` is a recommended update for all Ruby SAML users as it includes security fixes.
 
 Version `1.0` adds security improvements like entity expansion limitation, more SAML message validations, and other important improvements like decrypt support.
 
-For more details, please review [the changelog](changelog.md).
-
 ### Important Changes
 Please note the `get_idp_metadata` method raises an exception when it is not able to fetch the idp metadata, so review your integration if you are using this functionality.
 
@@ -31,6 +35,8 @@ We created a demo project for Rails4 that uses the latest version of this librar
 * 1.9.x
 * 2.1.x
 * 2.2.x
+* JRuby 1.7.19
+* JRuby 9.0.0.0
 
 ## Adding Features, Pull Requests
 * Fork the repository
@@ -96,7 +102,7 @@ To override the default behavior and control the destination of log messages, pr
 a ruby Logger object to the gem's logging singleton:
 
 ```ruby
-OneLogin::RubySaml::Logging.logger = Logger.new(File.open('/var/log/ruby-saml.log', 'w')
+OneLogin::RubySaml::Logging.logger = Logger.new(File.open('/var/log/ruby-saml.log', 'w'))
 ```
 
 ## The Initialization Phase
@@ -164,6 +170,13 @@ def saml_settings
 end
 ```
 
+Some assertion validations can be skipped by passing parameters to OneLogin::RubySaml::Response.new().  For example, you can skip the Conditions validation or the SubjectConfirmation validations by initializing the response with different options:
+
+```ruby
+response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
+response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
+```
+
 What's left at this point, is to wrap it all up in a controller and point the initialization and consumption URLs in OneLogin at that. A full controller example could look like this:
 
 ```ruby
@@ -239,9 +252,9 @@ def saml_settings
 end
 ```
 The following attributes are set:
-  * id_sso_target_url
+  * idp_sso_target_url
   * idp_slo_target_url
-  * id_cert_fingerpint
+  * idp_cert_fingerpint
 
 If you are using saml:AttributeStatement to transfer metadata, like the user name, you can access all the attributes through response.attributes. It contains all the saml:AttributeStatement with its 'Name' as a indifferent key the one/more saml:AttributeValue as value. The value returned depends on the value of the
 `single_value_compatibility` (when activate, only one value returned, the first one)
@@ -394,6 +407,7 @@ Service Provider.
 
 Notice that this toolkit uses 'settings.certificate' and 'settings.private_key' for the sign and the decrypt process.
 
+Enable/disable the soft mode by the settings.soft parameter. When is set false, the saml validations errors will raise an exception.
 
 ## Decrypting
 
diff --git a/Rakefile b/Rakefile
index 7c2e2c4..40fa256 100644
--- a/Rakefile
+++ b/Rakefile
@@ -25,17 +25,3 @@ end
 task :test
 
 task :default => :test
-
-# require 'rake/rdoctask'
-# Rake::RDocTask.new do |rdoc|
-#   if File.exist?('VERSION')
-#     version = File.read('VERSION')
-#   else
-#     version = ""
-#   end
-
-#   rdoc.rdoc_dir = 'rdoc'
-#   rdoc.title = "ruby-saml #{version}"
-#   rdoc.rdoc_files.include('README*')
-#   rdoc.rdoc_files.include('lib/**/*.rb')
-#end
diff --git a/changelog.md b/changelog.md
index 0033df5..97e2a03 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,25 @@
 # RubySaml Changelog
 
+### 1.1.2 (February 15, 2015)
+* Improve signature validation. Add tests.
+ [#302](https://github.com/onelogin/ruby-saml/pull/302) Add Destination validation.
+* [#292](https://github.com/onelogin/ruby-saml/pull/292) Improve the error message when validating the audience.
+* [#287](https://github.com/onelogin/ruby-saml/pull/287) Keep the extracted certificate when parsing IdP metadata.
+
+### 1.1.1 (November 10, 2015)
+* [#275](https://github.com/onelogin/ruby-saml/pull/275) Fix a bug on signature validations that invalidates valid SAML messages.
+
+### 1.1.0 (October 27, 2015)
+* [#273](https://github.com/onelogin/ruby-saml/pull/273) Support SAMLResponse without ds:x509certificate
+* [#270](https://github.com/onelogin/ruby-saml/pull/270) Allow SAML elements to come from any namespace (at decryption process)
+* [#261](https://github.com/onelogin/ruby-saml/pull/261) Allow validate_subject_confirmation Response validation to be skipped
+* [#258](https://github.com/onelogin/ruby-saml/pull/258) Fix allowed_clock_drift on the validate_session_expiration test
+* [#256](https://github.com/onelogin/ruby-saml/pull/256) Separate the create_authentication_xml_doc in two methods. 
+* [#255](https://github.com/onelogin/ruby-saml/pull/255) Refactor validate signature.
+* [#254](https://github.com/onelogin/ruby-saml/pull/254) Handle empty URI references 
+* [#251](https://github.com/onelogin/ruby-saml/pull/251) Support qualified and unqualified NameID in attributes
+* [#234](https://github.com/onelogin/ruby-saml/pull/234) Add explicit support for JRuby
+
 ### 1.0.0 (June 30, 2015)
 * [#247](https://github.com/onelogin/ruby-saml/pull/247) Avoid entity expansion (XEE attacks)
 * [#246](https://github.com/onelogin/ruby-saml/pull/246) Fix bug generating Logout Response (issuer was at wrong order)
diff --git a/lib/onelogin/ruby-saml/authrequest.rb b/lib/onelogin/ruby-saml/authrequest.rb
index 2b38ce9..b5108f4 100644
--- a/lib/onelogin/ruby-saml/authrequest.rb
+++ b/lib/onelogin/ruby-saml/authrequest.rb
@@ -87,6 +87,11 @@ module OneLogin
       # @return [String] The SAMLRequest String.
       #
       def create_authentication_xml_doc(settings)
+        document = create_xml_document(settings)
+        sign_document(document, settings)
+      end
+
+      def create_xml_document(settings)
         time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
 
         request_doc = XMLSecurity::Document.new
@@ -141,14 +146,18 @@ module OneLogin
           end
         end
 
+        request_doc
+      end
+
+      def sign_document(document, settings)
         # embed signature
         if settings.security[:authn_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign] 
           private_key = settings.get_sp_key
           cert = settings.get_sp_cert
-          request_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
+          document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
         end
 
-        request_doc
+        document
       end
 
     end
diff --git a/lib/onelogin/ruby-saml/idp_metadata_parser.rb b/lib/onelogin/ruby-saml/idp_metadata_parser.rb
index 42d4f02..7e48af9 100644
--- a/lib/onelogin/ruby-saml/idp_metadata_parser.rb
+++ b/lib/onelogin/ruby-saml/idp_metadata_parser.rb
@@ -44,6 +44,7 @@ module OneLogin
           settings.name_identifier_format = idp_name_id_format
           settings.idp_sso_target_url = single_signon_service_url
           settings.idp_slo_target_url = single_logout_service_url
+          settings.idp_cert = certificate_base64
           settings.idp_cert_fingerprint = fingerprint
         end
       end
@@ -133,19 +134,28 @@ module OneLogin
         node.value if node
       end
 
+      # @return [String|nil] Unformatted Certificate if exists
+      #
+      def certificate_base64
+        @certificate_base64 ||= begin
+          node = REXML::XPath.first(
+              document,
+              "/md:EntityDescriptor/md:IDPSSODescriptor/md:KeyDescriptor[@use='signing']/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
+              { "md" => METADATA, "ds" => DSIG }
+          )
+          node.text if node
+        end
+      end
+
       # @return [String|nil] X509Certificate if exists
       #
       def certificate
         @certificate ||= begin
-          node = REXML::XPath.first(
-            document,
-            "/md:EntityDescriptor/md:IDPSSODescriptor/md:KeyDescriptor[@use='signing']/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
-            { "md" => METADATA, "ds" => DSIG }
-          )
-          Base64.decode64(node.text) if node
+          Base64.decode64(certificate_base64) if certificate_base64
         end
       end
 
+
       # @return [String|nil] the SHA-1 fingerpint of the X509Certificate if it exists
       #
       def fingerprint
diff --git a/lib/onelogin/ruby-saml/response.rb b/lib/onelogin/ruby-saml/response.rb
index 3f58b23..f18c92b 100644
--- a/lib/onelogin/ruby-saml/response.rb
+++ b/lib/onelogin/ruby-saml/response.rb
@@ -36,7 +36,8 @@ module OneLogin
       # @param options  [Hash]   :settings to provide the OneLogin::RubySaml::Settings object 
       #                          Or some options for the response validation process like skip the conditions validation
       #                          with the :skip_conditions, or allow a clock_drift when checking dates with :allowed_clock_drift
-      #                          or :matches_request_id that will validate that the response matches the ID of the request.
+      #                          or :matches_request_id that will validate that the response matches the ID of the request,
+      #                          or skip the subject confirmation validation with the :skip_subject_confirmation option
       def initialize(response, options = {})
         @errors = []
 
@@ -94,6 +95,7 @@ module OneLogin
 
       alias_method :nameid, :name_id
 
+
       # Gets the SessionIndex from the AuthnStatement.
       # Could be used to be stored in the local session in order
       # to be used in a future Logout Request that the SP could
@@ -131,12 +133,22 @@ module OneLogin
           stmt_element.elements.each do |attr_element|
             name  = attr_element.attributes["Name"]
             values = attr_element.elements.collect{|e|
-              # SAMLCore requires that nil AttributeValues MUST contain xsi:nil XML attribute set to "true" or "1"
-              # otherwise the value is to be regarded as empty.
-              ["true", "1"].include?(e.attributes['xsi:nil']) ? nil : e.text.to_s
+              if (e.elements.nil? || e.elements.size == 0)
+                # SAMLCore requires that nil AttributeValues MUST contain xsi:nil XML attribute set to "true" or "1"
+                # otherwise the value is to be regarded as empty.
+                ["true", "1"].include?(e.attributes['xsi:nil']) ? nil : e.text.to_s
+              # explicitly support saml2:NameID with saml2:NameQualifier if supplied in attributes
+              # this is useful for allowing eduPersonTargetedId to be passed as an opaque identifier to use to 
+              # identify the subject in an SP rather than email or other less opaque attributes
+              # NameQualifier, if present is prefixed with a "/" to the value
+              else 
+               REXML::XPath.match(e,'a:NameID', { "a" => ASSERTION }).collect{|n|
+                  (n.attributes['NameQualifier'] ? n.attributes['NameQualifier'] +"/" : '') + n.text.to_s
+                }
+              end
             }
 
-            attributes.add(name, values)
+            attributes.add(name, values.flatten)
           end
 
           attributes
@@ -242,6 +254,19 @@ module OneLogin
         end
       end
 
+      # @return [String|nil] Destination attribute from the SAML Response.
+      #
+      def destination
+        @destination ||= begin
+          node = REXML::XPath.first(
+            document,
+            "/p:Response",
+            { "p" => PROTOCOL }
+          )
+          node.nil? ? nil : node.attributes['Destination']
+        end
+      end
+
       # @return [Array] The Audience elements from the Contitions of the SAML Response.
       #
       def audiences
@@ -283,6 +308,7 @@ module OneLogin
         validate_in_response_to &&
         validate_conditions &&
         validate_audience &&
+        validate_destination &&
         validate_issuer &&
         validate_session_expiration &&
         validate_subject_confirmation &&
@@ -316,11 +342,9 @@ module OneLogin
 
       # Validates that the SAML Response provided in the initialization is not empty,
       # also check that the setting and the IdP cert were also provided
-      # @param soft [Boolean] soft Enable or Disable the soft mode (In order to raise exceptions when the response is invalid or not)
-      # @return [Boolean] True if the required info is found, otherwise False if soft=True
-      # @raise [ValidationError] if soft == false and validation fails
+      # @return [Boolean] True if the required info is found, false otherwise
       #
-      def validate_response_state(soft = true)
+      def validate_response_state
         return append_error("Blank response") if response.nil? || response.empty?
 
         return append_error("No settings on response") if settings.nil?
@@ -444,7 +468,22 @@ module OneLogin
         return true if audiences.empty? || settings.issuer.nil? || settings.issuer.empty?
 
         unless audiences.include? settings.issuer
-          error_msg = "#{settings.issuer} is not a valid audience for this Response"
+          error_msg = "#{settings.issuer} is not a valid audience for this Response - Valid audiences: #{audiences.join(',')}"
+          return append_error(error_msg)
+        end
+
+        true
+      end
+
+      # Validates the Destination, (If the SAML Response is received where expected)
+      # If fails, the error is added to the errors array
+      # @return [Boolean] True if there is a Destination element that matches the Consumer Service URL, otherwise False
+      #
+      def validate_destination
+        return true if destination.nil? || destination.empty? || settings.assertion_consumer_service_url.nil? || settings.assertion_consumer_service_url.empty?
+
+        unless destination == settings.assertion_consumer_service_url
+          error_msg = "The response was received at #{destination} instead of #{settings.assertion_consumer_service_url}"
           return append_error(error_msg)
         end
 
@@ -504,7 +543,7 @@ module OneLogin
         return true if session_expires_at.nil?
 
         now = Time.now.utc
-        unless session_expires_at > (now + allowed_clock_drift)
+        unless (session_expires_at + allowed_clock_drift) > now
           error_msg = "The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response"
           return append_error(error_msg)
         end
@@ -513,12 +552,14 @@ module OneLogin
       end
 
       # Validates if exists valid SubjectConfirmation (If the response was initialized with the :allowed_clock_drift option,
-      # timimg validation are relaxed by the allowed_clock_drift value)
+      # timimg validation are relaxed by the allowed_clock_drift value. If the response was initialized with the 
+      # :skip_subject_confirmation option, this validation is skipped)
       # If fails, the error is added to the errors array
       # @return [Boolean] True if exists a valid SubjectConfirmation, otherwise False if soft=True
       # @raise [ValidationError] if soft == false and validation fails
       #
       def validate_subject_confirmation
+        return true if options[:skip_subject_confirmation]
         valid_subject_confirmation = false
 
         subject_confirmation_nodes = xpath_from_signed_assertion('/a:Subject/a:SubjectConfirmation')
@@ -559,20 +600,38 @@ module OneLogin
       # @raise [ValidationError] if soft == false and validation fails
       #
       def validate_signature
-        fingerprint = settings.get_fingerprint
+        error_msg = "Invalid Signature on SAML Response"
 
         # If the response contains the signature, and the assertion was encrypted, validate the original SAML Response
         # otherwise, review if the decrypted assertion contains a signature
-        response_signed = REXML::XPath.first(
+        sig_elements = REXML::XPath.match(
           document,
-          "/p:Response[@ID=$id]",
-          { "p" => PROTOCOL, "ds" => DSIG },
-          { 'id' => document.signed_element_id }
+          "/p:Response/ds:Signature]",
+          { "p" => PROTOCOL, "ds" => DSIG }
         )
-        doc = (response_signed || decrypted_document.nil?) ? document : decrypted_document
 
-        unless fingerprint && doc.validate_document(fingerprint, :fingerprint_alg => settings.idp_cert_fingerprint_algorithm)
-          error_msg = "Invalid Signature on SAML Response"
+        use_original = sig_elements.size == 1 || decrypted_document.nil?
+        doc = use_original ? document : decrypted_document
+
+        # Check signature nodes
+        if sig_elements.nil? || sig_elements.size == 0
+          sig_elements = REXML::XPath.match(
+            doc,
+            "/p:Response/a:Assertion/ds:Signature",
+            {"p" => PROTOCOL, "a" => ASSERTION, "ds"=>DSIG}
+          )
+        end
+
+        if sig_elements.size != 1
+          return append_error(error_msg)
+        end
+
+        opts = {}
+        opts[:fingerprint_alg] = settings.idp_cert_fingerprint_algorithm
+        opts[:cert] = settings.get_idp_cert
+        fingerprint = settings.get_fingerprint
+
+        unless fingerprint && doc.validate_document(fingerprint, @soft, opts)          
           return append_error(error_msg)
         end
 
@@ -676,7 +735,7 @@ module OneLogin
       # @return [REXML::Document] The decrypted EncryptedAssertion element
       #
       def decrypt_assertion(encrypted_assertion_node)
-        decrypt_element(encrypted_assertion_node, /(.*<\/(saml2*:|)Assertion>)/m)
+        decrypt_element(encrypted_assertion_node, /(.*<\/(\w+:)?Assertion>)/m)
       end
 
       # Decrypts an EncryptedID element
@@ -684,7 +743,7 @@ module OneLogin
       # @return [REXML::Document] The decrypted EncrypedtID element
       #
       def decrypt_nameid(encryptedid_node)
-        decrypt_element(encryptedid_node, /(.*<\/(saml2*:|)NameID>)/m)
+        decrypt_element(encryptedid_node, /(.*<\/(\w+:)?NameID>)/m)
       end
 
       # Decrypt an element
diff --git a/lib/onelogin/ruby-saml/saml_message.rb b/lib/onelogin/ruby-saml/saml_message.rb
index 89948a7..9739564 100644
--- a/lib/onelogin/ruby-saml/saml_message.rb
+++ b/lib/onelogin/ruby-saml/saml_message.rb
@@ -23,7 +23,7 @@ module OneLogin
       # @return [Nokogiri::XML::Schema] Gets the schema object of the SAML 2.0 Protocol schema
       #
       def self.schema
-        @schema ||= Mutex.new.synchronize do
+        Mutex.new.synchronize do
           Dir.chdir(File.expand_path("../../../schemas", __FILE__)) do
             ::Nokogiri::XML::Schema(File.read("saml-schema-protocol-2.0.xsd"))
           end
diff --git a/lib/onelogin/ruby-saml/settings.rb b/lib/onelogin/ruby-saml/settings.rb
index 56a2ca6..b587ffa 100644
--- a/lib/onelogin/ruby-saml/settings.rb
+++ b/lib/onelogin/ruby-saml/settings.rb
@@ -118,8 +118,8 @@ module OneLogin
       def get_idp_cert
         return nil if idp_cert.nil? || idp_cert.empty?
 
-        formated_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert)
-        OpenSSL::X509::Certificate.new(formated_cert)
+        formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert)
+        OpenSSL::X509::Certificate.new(formatted_cert)
       end
 
       # @return [OpenSSL::X509::Certificate|nil] Build the SP certificate from the settings (previously format it)
@@ -127,8 +127,8 @@ module OneLogin
       def get_sp_cert
         return nil if certificate.nil? || certificate.empty?
 
-        formated_cert = OneLogin::RubySaml::Utils.format_cert(certificate)
-        OpenSSL::X509::Certificate.new(formated_cert)
+        formatted_cert = OneLogin::RubySaml::Utils.format_cert(certificate)
+        OpenSSL::X509::Certificate.new(formatted_cert)
       end
 
       # @return [OpenSSL::PKey::RSA] Build the SP private from the settings (previously format it)
diff --git a/lib/onelogin/ruby-saml/utils.rb b/lib/onelogin/ruby-saml/utils.rb
index 430ade0..4d7161d 100644
--- a/lib/onelogin/ruby-saml/utils.rb
+++ b/lib/onelogin/ruby-saml/utils.rb
@@ -65,7 +65,7 @@ module OneLogin
       # @option params [OpenSSL::X509::Certificate] cert The Identity provider public certtificate
       # @option params [String] sig_alg The SigAlg parameter
       # @option params [String] signature The Signature parameter (base64 encoded)
-      # @option params [String] query_string The SigAlg parameter
+      # @option params [String] query_string The full GET Query String to be compared
       # @return [Boolean] True if the Signature is valid, False otherwise
       #
       def self.verify_signature(params)
diff --git a/lib/onelogin/ruby-saml/version.rb b/lib/onelogin/ruby-saml/version.rb
index 8ed121a..8a10ff4 100644
--- a/lib/onelogin/ruby-saml/version.rb
+++ b/lib/onelogin/ruby-saml/version.rb
@@ -1,5 +1,5 @@
 module OneLogin
   module RubySaml
-    VERSION = '1.0.0'
+    VERSION = '1.1.2'
   end
 end
diff --git a/lib/xml_security.rb b/lib/xml_security.rb
index 4136504..3e83698 100644
--- a/lib/xml_security.rb
+++ b/lib/xml_security.rb
@@ -199,123 +199,128 @@ module XMLSecurity
         "//ds:X509Certificate",
         { "ds"=>DSIG }
       )
-      unless cert_element
-        if soft
-          return false
+
+      if cert_element        
+        base64_cert = cert_element.text
+        cert_text = Base64.decode64(base64_cert)
+        cert = OpenSSL::X509::Certificate.new(cert_text)
+
+        if options[:fingerprint_alg]
+          fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new
         else
-          raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate)")
+          fingerprint_alg = OpenSSL::Digest::SHA1.new
         end
-      end
-      base64_cert = cert_element.text
-      cert_text = Base64.decode64(base64_cert)
-      cert = OpenSSL::X509::Certificate.new(cert_text)
+        fingerprint = fingerprint_alg.hexdigest(cert.to_der)
 
-      if options[:fingerprint_alg]
-        fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new
+        # check cert matches registered idp cert
+        if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
+          @errors << "Fingerprint mismatch"
+          return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch"))
+        end
       else
-        fingerprint_alg = OpenSSL::Digest::SHA1.new
-      end
-      fingerprint = fingerprint_alg.hexdigest(cert.to_der)
-
-      # check cert matches registered idp cert
-      if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
-        @errors << "Fingerprint mismatch"
-        return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Fingerprint mismatch"))
+        if options[:cert]
+          base64_cert = Base64.encode64(options[:cert].to_pem)
+        else
+          if soft
+            return false
+          else
+            raise OneLogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate) and not cert provided at settings")
+          end          
+        end
       end
-
       validate_signature(base64_cert, soft)
     end
 
     def validate_signature(base64_cert, soft = true)
-      # validate references
-
-      # check for inclusive namespaces
-      inclusive_namespaces = extract_inclusive_namespaces
 
       document = Nokogiri.parse(self.to_s) do |options|
         options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
       end
 
-      # create a working copy so we don't modify the original
+      # create a rexml document
       @working_copy ||= REXML::Document.new(self.to_s).root
 
-      # store and remove signature node
-      @sig_element ||= begin
-        element = REXML::XPath.first(
+      # get signature node
+      sig_element = REXML::XPath.first(
           @working_copy,
           "//ds:Signature",
           {"ds"=>DSIG}
-        )
-        element.remove
-      end
+      )
 
-      # verify signature
-      signed_info_element = REXML::XPath.first(
-        @sig_element,
-        "//ds:SignedInfo",
+      # signature method
+      sig_alg_value = REXML::XPath.first(
+        sig_element,
+        "./ds:SignedInfo/ds:SignatureMethod",
         {"ds"=>DSIG}
       )
-      noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
-      noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
+      signature_algorithm = algorithm(sig_alg_value)
+
+      # get signature
+      base64_signature = REXML::XPath.first(
+        sig_element,
+        "./ds:SignatureValue",
+        {"ds" => DSIG}
+      ).text
+      signature = Base64.decode64(base64_signature)
+
+      # canonicalization method
       canon_algorithm = canon_algorithm REXML::XPath.first(
-        @sig_element,
-        '//ds:CanonicalizationMethod',
+        sig_element,
+        './ds:SignedInfo/ds:CanonicalizationMethod',
         'ds' => DSIG
       )
+
+      noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
+      noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
+
+      # Handle when no URI
+      noko_signed_info_reference_element_uri_attr = noko_signed_info_element.at_xpath('./ds:Reference', 'ds' => DSIG).attributes["URI"]
+      if (noko_signed_info_reference_element_uri_attr.value.empty?)
+        noko_signed_info_reference_element_uri_attr.value = "##{document.root.attribute('ID')}"
+      end
+
       canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
       noko_sig_element.remove
 
+      # get inclusive namespaces
+      inclusive_namespaces = extract_inclusive_namespaces
+
       # check digests
-      REXML::XPath.each(@sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
-        uri = ref.attributes.get_attribute("URI").value
-
-        hashed_element = document.at_xpath("//*[@ID=$uri]", nil, { 'uri' => uri[1..-1] })
-        canon_algorithm = canon_algorithm REXML::XPath.first(
-          ref,
-          '//ds:CanonicalizationMethod',
-          { "ds" => DSIG }
-        )
-        canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
-
-        digest_algorithm = algorithm(REXML::XPath.first(
-          ref,
-          "//ds:DigestMethod",
-          { "ds" => DSIG }
-        ))
-        hash = digest_algorithm.digest(canon_hashed_element)
-        encoded_digest_value = REXML::XPath.first(
-          ref,
-          "//ds:DigestValue",
-          { "ds" => DSIG }
-        ).text
-        digest_value = Base64.decode64(encoded_digest_value)
-
-        unless digests_match?(hash, digest_value)
-          @errors << "Digest mismatch"
-          return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Digest mismatch"))
-        end
-      end
+      ref = REXML::XPath.first(sig_element, "//ds:Reference", {"ds"=>DSIG})
+      uri = ref.attributes.get_attribute("URI").value
 
-      base64_signature = REXML::XPath.first(
-        @sig_element,
-        "//ds:SignatureValue",
-        {"ds" => DSIG}
+      hashed_element = uri.empty? ? document : document.at_xpath("//*[@ID=$uri]", nil, { 'uri' => uri[1..-1] })
+      # hashed_element = document.at_xpath("//*[@ID=$uri]", nil, { 'uri' => uri[1..-1] })
+      canon_algorithm = canon_algorithm REXML::XPath.first(
+        ref,
+        '//ds:CanonicalizationMethod',
+        { "ds" => DSIG }
+      )
+      canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
+
+      digest_algorithm = algorithm(REXML::XPath.first(
+        ref,
+        "//ds:DigestMethod",
+        { "ds" => DSIG }
+      ))
+      hash = digest_algorithm.digest(canon_hashed_element)
+      encoded_digest_value = REXML::XPath.first(
+        ref,
+        "//ds:DigestValue",
+        { "ds" => DSIG }
       ).text
+      digest_value = Base64.decode64(encoded_digest_value)
 
-      signature = Base64.decode64(base64_signature)
+      unless digests_match?(hash, digest_value)
+        @errors << "Digest mismatch"
+        return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Digest mismatch"))
+      end
 
       # get certificate object
       cert_text = Base64.decode64(base64_cert)
       cert = OpenSSL::X509::Certificate.new(cert_text)
 
-      # signature method
-      sig_alg_value = REXML::XPath.first(
-        signed_info_element,
-        "//ds:SignatureMethod",
-        {"ds"=>DSIG}
-      )
-      signature_algorithm = algorithm(sig_alg_value)
-
+      # verify signature
       unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
         @errors << "Key validation error"
         return soft ? false : (raise OneLogin::RubySaml::ValidationError.new("Key validation error"))
@@ -336,7 +341,11 @@ module XMLSecurity
         "//ds:Signature/ds:SignedInfo/ds:Reference",
         {"ds"=>DSIG}
       )
-      self.signed_element_id = reference_element.attribute("URI").value[1..-1] unless reference_element.nil?
+
+      return nil if reference_element.nil?
+
+      sei = reference_element.attribute("URI").value[1..-1] 
+      sei.nil? ? self.root.attribute("ID") : sei
     end
 
     def extract_inclusive_namespaces
@@ -349,7 +358,7 @@ module XMLSecurity
         prefix_list = element.attributes.get_attribute("PrefixList").value
         prefix_list.split(" ")
       else
-        []
+        nil
       end
     end
 
diff --git a/metadata.yml b/metadata.yml
deleted file mode 100644
index a321dc3..0000000
--- a/metadata.yml
+++ /dev/null
@@ -1,414 +0,0 @@
---- !ruby/object:Gem::Specification
-name: ruby-saml
-version: !ruby/object:Gem::Version
-  version: 1.0.0
-platform: ruby
-authors:
-- OneLogin LLC
-autorequire: 
-bindir: bin
-cert_chain: []
-date: 2015-07-07 00:00:00.000000000 Z
-dependencies:
-- !ruby/object:Gem::Dependency
-  name: uuid
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '2.3'
-  type: :runtime
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '2.3'
-- !ruby/object:Gem::Dependency
-  name: nokogiri
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: 1.5.10
-  type: :runtime
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: 1.5.10
-- !ruby/object:Gem::Dependency
-  name: minitest
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '5.5'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '5.5'
-- !ruby/object:Gem::Dependency
-  name: mocha
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '0.14'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '0.14'
-- !ruby/object:Gem::Dependency
-  name: rake
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '10'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '10'
-- !ruby/object:Gem::Dependency
-  name: shoulda
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '2.11'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '2.11'
-- !ruby/object:Gem::Dependency
-  name: simplecov
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: 0.9.0
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: 0.9.0
-- !ruby/object:Gem::Dependency
-  name: systemu
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '2'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "~>"
-      - !ruby/object:Gem::Version
-        version: '2'
-- !ruby/object:Gem::Dependency
-  name: timecop
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - "<="
-      - !ruby/object:Gem::Version
-        version: 0.6.0
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - "<="
-      - !ruby/object:Gem::Version
-        version: 0.6.0
-- !ruby/object:Gem::Dependency
-  name: pry-byebug
-  requirement: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-  type: :development
-  prerelease: false
-  version_requirements: !ruby/object:Gem::Requirement
-    requirements:
-    - - ">="
-      - !ruby/object:Gem::Version
-        version: '0'
-description: SAML toolkit for Ruby on Rails
-email: support at onelogin.com
-executables: []
-extensions: []
-extra_rdoc_files:
-- LICENSE
-- README.md
-files:
-- ".document"
-- ".gitignore"
-- ".travis.yml"
-- Gemfile
-- LICENSE
-- README.md
-- Rakefile
-- changelog.md
-- gemfiles/nokogiri-1.5.gemfile
-- lib/onelogin/ruby-saml.rb
-- lib/onelogin/ruby-saml/attribute_service.rb
-- lib/onelogin/ruby-saml/attributes.rb
-- lib/onelogin/ruby-saml/authrequest.rb
-- lib/onelogin/ruby-saml/http_error.rb
-- lib/onelogin/ruby-saml/idp_metadata_parser.rb
-- lib/onelogin/ruby-saml/logging.rb
-- lib/onelogin/ruby-saml/logoutrequest.rb
-- lib/onelogin/ruby-saml/logoutresponse.rb
-- lib/onelogin/ruby-saml/metadata.rb
-- lib/onelogin/ruby-saml/response.rb
-- lib/onelogin/ruby-saml/saml_message.rb
-- lib/onelogin/ruby-saml/settings.rb
-- lib/onelogin/ruby-saml/slo_logoutrequest.rb
-- lib/onelogin/ruby-saml/slo_logoutresponse.rb
-- lib/onelogin/ruby-saml/utils.rb
-- lib/onelogin/ruby-saml/validation_error.rb
-- lib/onelogin/ruby-saml/version.rb
-- lib/ruby-saml.rb
-- lib/schemas/saml-schema-assertion-2.0.xsd
-- lib/schemas/saml-schema-authn-context-2.0.xsd
-- lib/schemas/saml-schema-authn-context-types-2.0.xsd
-- lib/schemas/saml-schema-metadata-2.0.xsd
-- lib/schemas/saml-schema-protocol-2.0.xsd
-- lib/schemas/sstc-metadata-attr.xsd
-- lib/schemas/sstc-saml-attribute-ext.xsd
-- lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd
-- lib/schemas/sstc-saml-metadata-ui-v1.0.xsd
-- lib/schemas/xenc-schema.xsd
-- lib/schemas/xml.xsd
-- lib/schemas/xmldsig-core-schema.xsd
-- lib/xml_security.rb
-- ruby-saml.gemspec
-- test/certificates/certificate1
-- test/certificates/certificate_without_head_foot
-- test/certificates/formatted_certificate
-- test/certificates/formatted_private_key
-- test/certificates/formatted_rsa_private_key
-- test/certificates/invalid_certificate1
-- test/certificates/invalid_certificate2
-- test/certificates/invalid_certificate3
-- test/certificates/invalid_private_key1
-- test/certificates/invalid_private_key2
-- test/certificates/invalid_private_key3
-- test/certificates/invalid_rsa_private_key1
-- test/certificates/invalid_rsa_private_key2
-- test/certificates/invalid_rsa_private_key3
-- test/certificates/ruby-saml.crt
-- test/certificates/ruby-saml.key
-- test/idp_metadata_parser_test.rb
-- test/logging_test.rb
-- test/logout_requests/invalid_slo_request.xml
-- test/logout_requests/slo_request.xml
-- test/logout_requests/slo_request.xml.base64
-- test/logout_requests/slo_request_deflated.xml.base64
-- test/logout_requests/slo_request_with_session_index.xml
-- test/logout_responses/logoutresponse_fixtures.rb
-- test/logoutrequest_test.rb
-- test/logoutresponse_test.rb
-- test/metadata_test.rb
-- test/request_test.rb
-- test/response_test.rb
-- test/responses/adfs_response_sha1.xml
-- test/responses/adfs_response_sha256.xml
-- test/responses/adfs_response_sha384.xml
-- test/responses/adfs_response_sha512.xml
-- test/responses/adfs_response_xmlns.xml
-- test/responses/attackxee.xml
-- test/responses/idp_descriptor.xml
-- test/responses/invalids/invalid_audience.xml.base64
-- test/responses/invalids/invalid_issuer_assertion.xml.base64
-- test/responses/invalids/invalid_issuer_message.xml.base64
-- test/responses/invalids/invalid_signature_position.xml.base64
-- test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64
-- test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64
-- test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64
-- test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64
-- test/responses/invalids/multiple_assertions.xml.base64
-- test/responses/invalids/multiple_signed.xml.base64
-- test/responses/invalids/no_id.xml.base64
-- test/responses/invalids/no_saml2.xml.base64
-- test/responses/invalids/no_signature.xml.base64
-- test/responses/invalids/no_status.xml.base64
-- test/responses/invalids/no_status_code.xml.base64
-- test/responses/invalids/no_subjectconfirmation_data.xml.base64
-- test/responses/invalids/no_subjectconfirmation_method.xml.base64
-- test/responses/invalids/response_encrypted_attrs.xml.base64
-- test/responses/invalids/response_invalid_signed_element.xml.base64
-- test/responses/invalids/status_code_responder.xml.base64
-- test/responses/invalids/status_code_responer_and_msg.xml.base64
-- test/responses/no_signature_ns.xml
-- test/responses/open_saml_response.xml
-- test/responses/response_assertion_wrapped.xml.base64
-- test/responses/response_encrypted_nameid.xml.base64
-- test/responses/response_eval.xml
-- test/responses/response_no_cert_and_encrypted_attrs.xml
-- test/responses/response_unsigned_xml_base64
-- test/responses/response_with_ampersands.xml
-- test/responses/response_with_ampersands.xml.base64
-- test/responses/response_with_multiple_attribute_values.xml
-- test/responses/response_with_saml2_namespace.xml.base64
-- test/responses/response_with_signed_assertion.xml.base64
-- test/responses/response_with_signed_assertion_2.xml.base64
-- test/responses/response_with_undefined_recipient.xml.base64
-- test/responses/response_without_attributes.xml.base64
-- test/responses/response_wrapped.xml.base64
-- test/responses/signed_message_encrypted_signed_assertion.xml.base64
-- test/responses/signed_message_encrypted_unsigned_assertion.xml.base64
-- test/responses/simple_saml_php.xml
-- test/responses/starfield_response.xml.base64
-- test/responses/test_sign.xml
-- test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64
-- test/responses/unsigned_message_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64
-- test/responses/valid_response.xml.base64
-- test/saml_message_test.rb
-- test/settings_test.rb
-- test/slo_logoutrequest_test.rb
-- test/slo_logoutresponse_test.rb
-- test/test_helper.rb
-- test/utils_test.rb
-- test/xml_security_test.rb
-homepage: http://github.com/onelogin/ruby-saml
-licenses:
-- MIT
-metadata: {}
-post_install_message: 
-rdoc_options:
-- "--charset=UTF-8"
-require_paths:
-- lib
-required_ruby_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: 1.8.7
-required_rubygems_version: !ruby/object:Gem::Requirement
-  requirements:
-  - - ">="
-    - !ruby/object:Gem::Version
-      version: '0'
-requirements: []
-rubyforge_project: http://www.rubygems.org/gems/ruby-saml
-rubygems_version: 2.4.5
-signing_key: 
-specification_version: 4
-summary: SAML Ruby Tookit
-test_files:
-- test/certificates/certificate1
-- test/certificates/certificate_without_head_foot
-- test/certificates/formatted_certificate
-- test/certificates/formatted_private_key
-- test/certificates/formatted_rsa_private_key
-- test/certificates/invalid_certificate1
-- test/certificates/invalid_certificate2
-- test/certificates/invalid_certificate3
-- test/certificates/invalid_private_key1
-- test/certificates/invalid_private_key2
-- test/certificates/invalid_private_key3
-- test/certificates/invalid_rsa_private_key1
-- test/certificates/invalid_rsa_private_key2
-- test/certificates/invalid_rsa_private_key3
-- test/certificates/ruby-saml.crt
-- test/certificates/ruby-saml.key
-- test/idp_metadata_parser_test.rb
-- test/logging_test.rb
-- test/logout_requests/invalid_slo_request.xml
-- test/logout_requests/slo_request.xml
-- test/logout_requests/slo_request.xml.base64
-- test/logout_requests/slo_request_deflated.xml.base64
-- test/logout_requests/slo_request_with_session_index.xml
-- test/logout_responses/logoutresponse_fixtures.rb
-- test/logoutrequest_test.rb
-- test/logoutresponse_test.rb
-- test/metadata_test.rb
-- test/request_test.rb
-- test/response_test.rb
-- test/responses/adfs_response_sha1.xml
-- test/responses/adfs_response_sha256.xml
-- test/responses/adfs_response_sha384.xml
-- test/responses/adfs_response_sha512.xml
-- test/responses/adfs_response_xmlns.xml
-- test/responses/attackxee.xml
-- test/responses/idp_descriptor.xml
-- test/responses/invalids/invalid_audience.xml.base64
-- test/responses/invalids/invalid_issuer_assertion.xml.base64
-- test/responses/invalids/invalid_issuer_message.xml.base64
-- test/responses/invalids/invalid_signature_position.xml.base64
-- test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64
-- test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64
-- test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64
-- test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64
-- test/responses/invalids/multiple_assertions.xml.base64
-- test/responses/invalids/multiple_signed.xml.base64
-- test/responses/invalids/no_id.xml.base64
-- test/responses/invalids/no_saml2.xml.base64
-- test/responses/invalids/no_signature.xml.base64
-- test/responses/invalids/no_status.xml.base64
-- test/responses/invalids/no_status_code.xml.base64
-- test/responses/invalids/no_subjectconfirmation_data.xml.base64
-- test/responses/invalids/no_subjectconfirmation_method.xml.base64
-- test/responses/invalids/response_encrypted_attrs.xml.base64
-- test/responses/invalids/response_invalid_signed_element.xml.base64
-- test/responses/invalids/status_code_responder.xml.base64
-- test/responses/invalids/status_code_responer_and_msg.xml.base64
-- test/responses/no_signature_ns.xml
-- test/responses/open_saml_response.xml
-- test/responses/response_assertion_wrapped.xml.base64
-- test/responses/response_encrypted_nameid.xml.base64
-- test/responses/response_eval.xml
-- test/responses/response_no_cert_and_encrypted_attrs.xml
-- test/responses/response_unsigned_xml_base64
-- test/responses/response_with_ampersands.xml
-- test/responses/response_with_ampersands.xml.base64
-- test/responses/response_with_multiple_attribute_values.xml
-- test/responses/response_with_saml2_namespace.xml.base64
-- test/responses/response_with_signed_assertion.xml.base64
-- test/responses/response_with_signed_assertion_2.xml.base64
-- test/responses/response_with_undefined_recipient.xml.base64
-- test/responses/response_without_attributes.xml.base64
-- test/responses/response_wrapped.xml.base64
-- test/responses/signed_message_encrypted_signed_assertion.xml.base64
-- test/responses/signed_message_encrypted_unsigned_assertion.xml.base64
-- test/responses/simple_saml_php.xml
-- test/responses/starfield_response.xml.base64
-- test/responses/test_sign.xml
-- test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64
-- test/responses/unsigned_message_encrypted_signed_assertion.xml.base64
-- test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64
-- test/responses/valid_response.xml.base64
-- test/saml_message_test.rb
-- test/settings_test.rb
-- test/slo_logoutrequest_test.rb
-- test/slo_logoutresponse_test.rb
-- test/test_helper.rb
-- test/utils_test.rb
-- test/xml_security_test.rb
diff --git a/ruby-saml.gemspec b/ruby-saml.gemspec
index ca66d88..fbb3fd1 100644
--- a/ruby-saml.gemspec
+++ b/ruby-saml.gemspec
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
   s.license = 'MIT'
   s.extra_rdoc_files = [
     "LICENSE",
-     "README.md"
+    "README.md"
   ]
   s.files = `git ls-files`.split("\n")
   s.homepage = %q{http://github.com/onelogin/ruby-saml}
@@ -30,7 +30,12 @@ Gem::Specification.new do |s|
   # Because runtime dependencies are determined at build time, we cannot make
   # Nokogiri's version dependent on the Ruby version, even though we would
   # have liked to constrain Ruby 1.8.7 to install only the 1.5.x versions.
-  s.add_runtime_dependency('nokogiri', '>= 1.5.10')
+  if defined?(JRUBY_VERSION)
+    s.add_runtime_dependency('nokogiri', '>= 1.6.0')
+    s.add_runtime_dependency('jruby-openssl', '>= 0.9.8')
+  else
+    s.add_runtime_dependency('nokogiri', '>= 1.5.10')
+  end
 
   s.add_development_dependency('minitest', '~> 5.5')
   s.add_development_dependency('mocha',    '~> 0.14')
@@ -40,7 +45,10 @@ Gem::Specification.new do |s|
   s.add_development_dependency('systemu',  '~> 2')
   s.add_development_dependency('timecop',  '<= 0.6.0')
 
-  if RUBY_VERSION < '1.9'
+  if defined?(JRUBY_VERSION)
+    # All recent versions of JRuby play well with pry
+    s.add_development_dependency('pry')
+  elsif RUBY_VERSION < '1.9'
     # 1.8.7
     s.add_development_dependency('ruby-debug', '~> 0.10.4')
   elsif RUBY_VERSION < '2.0'
diff --git a/test/response_test.rb b/test/response_test.rb
index 0e81cb1..13b6f66 100644
--- a/test/response_test.rb
+++ b/test/response_test.rb
@@ -9,11 +9,14 @@ class RubySamlTest < Minitest::Test
     let(:settings) { OneLogin::RubySaml::Settings.new }
     let(:response) { OneLogin::RubySaml::Response.new(response_document_without_recipient) }
     let(:response_without_attributes) { OneLogin::RubySaml::Response.new(response_document_without_attributes) }
+    let(:response_without_reference_uri) { OneLogin::RubySaml::Response.new(response_document_without_reference_uri) }
     let(:response_with_signed_assertion) { OneLogin::RubySaml::Response.new(response_document_with_signed_assertion) }
+    let(:response_with_ds_namespace_at_the_root) { OneLogin::RubySaml::Response.new(response_document_with_ds_namespace_at_the_root)}
     let(:response_unsigned) { OneLogin::RubySaml::Response.new(response_document_unsigned) }
     let(:response_wrapped) { OneLogin::RubySaml::Response.new(response_document_wrapped) }
     let(:response_multiple_attr_values) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) }
     let(:response_valid_signed) { OneLogin::RubySaml::Response.new(response_document_valid_signed) }
+    let(:response_valid_signed_without_x509certificate) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) }
     let(:response_no_id) { OneLogin::RubySaml::Response.new(read_invalid_response("no_id.xml.base64")) }
     let(:response_no_version) { OneLogin::RubySaml::Response.new(read_invalid_response("no_saml2.xml.base64")) }
     let(:response_multi_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_assertions.xml.base64")) }
@@ -55,7 +58,7 @@ class RubySamlTest < Minitest::Test
 
     describe "Prevent XEE attack" do
       before do
-        @response = OneLogin::RubySaml::Response.new(fixture(:attackxee))        
+        @response = OneLogin::RubySaml::Response.new(fixture(:attackxee))
       end
 
       it "false when evil attack vector is present, soft = true" do
@@ -201,7 +204,7 @@ class RubySamlTest < Minitest::Test
           settings.issuer = 'invalid'
           response_valid_signed.settings = settings
           response_valid_signed.soft = false
-          error_msg = "#{response_valid_signed.settings.issuer} is not a valid audience for this Response"
+          error_msg = "#{response_valid_signed.settings.issuer} is not a valid audience for this Response - Valid audiences: https://someone.example.com/audience"
           assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
             response_valid_signed.is_valid?
           end
@@ -365,7 +368,7 @@ class RubySamlTest < Minitest::Test
           settings.issuer = 'invalid'
           response_valid_signed.settings = settings
           response_valid_signed.is_valid?
-          assert_includes response_valid_signed.errors, "#{response_valid_signed.settings.issuer} is not a valid audience for this Response"
+          assert_includes response_valid_signed.errors, "#{response_valid_signed.settings.issuer} is not a valid audience for this Response - Valid audiences: https://someone.example.com/audience"
         end
 
         it "return false when no ID present in the SAML Response" do
@@ -384,6 +387,15 @@ class RubySamlTest < Minitest::Test
           response_no_version.is_valid?
           assert_includes response_no_version.errors, "Unsupported SAML version"
         end
+
+        it "return true when a nil URI is given in the ds:Reference" do
+
+          response_without_reference_uri.stubs(:conditions).returns(nil)
+          response_without_reference_uri.settings = settings
+          response_without_reference_uri.settings.idp_cert_fingerprint = "19:4D:97:E4:D8:C9:C8:CF:A4:B7:21:E5:EE:49:7F:D9:66:0E:52:13"
+          assert response_without_reference_uri.is_valid?
+          assert_empty response_without_reference_uri.errors
+        end
       end
     end
 
@@ -399,7 +411,22 @@ class RubySamlTest < Minitest::Test
         response.settings = settings
         response.settings.issuer = 'invalid_audience'
         assert !response.send(:validate_audience)
-        assert_includes response.errors, "#{response.settings.issuer} is not a valid audience for this Response"
+        assert_includes response.errors, "#{response.settings.issuer} is not a valid audience for this Response - Valid audiences: {audience}"
+      end
+    end
+
+    describe "#validate_destination" do
+      it "return true when the destination of the SAML Response matches the assertion consumer service url" do
+        response.settings = settings
+        assert response.send(:validate_destination)
+        assert_empty response.errors
+      end
+
+      it "return false when the destination of the SAML Response does not match the assertion consumer service url" do
+        response.settings = settings
+        response.settings.assertion_consumer_service_url = 'invalid_acs'
+        assert !response.send(:validate_destination)
+        assert_includes response.errors, "The response was received at #{response.destination} instead of #{response.settings.assertion_consumer_service_url}"
       end
     end
 
@@ -409,7 +436,7 @@ class RubySamlTest < Minitest::Test
         assert response_valid_signed.send(:validate_issuer)
 
         response_valid_signed.settings.idp_entity_id = 'https://app.onelogin.com/saml2'
-        assert response_valid_signed.send(:validate_issuer)        
+        assert response_valid_signed.send(:validate_issuer)
       end
 
       it "return false when the issuer of the Message does not match the IdP entityId" do
@@ -491,7 +518,7 @@ class RubySamlTest < Minitest::Test
         response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings, :matches_request_id => "_fc4a34b0-7efb-012e-caae-782bcb13bb38")
         assert response.send(:validate_in_response_to)
         assert_empty response.errors
-      end      
+      end
 
       it "return true when no Request ID is provided for checking" do
         response = OneLogin::RubySaml::Response.new(response_document_valid_signed, :settings => settings)
@@ -539,7 +566,7 @@ class RubySamlTest < Minitest::Test
         response_invalid_audience.settings = settings
         response_invalid_audience.settings.issuer = "https://invalid.example.com/audience"
         assert !response_invalid_audience.send(:validate_audience)
-        assert_includes response_invalid_audience.errors, "#{response_invalid_audience.settings.issuer} is not a valid audience for this Response"
+        assert_includes response_invalid_audience.errors, "#{response_invalid_audience.settings.issuer} is not a valid audience for this Response - Valid audiences: http://invalid.audience.com"
       end
     end
 
@@ -606,6 +633,27 @@ class RubySamlTest < Minitest::Test
         assert !response_invalid_subjectconfirmation_noa.send(:validate_subject_confirmation)
         assert_includes response_invalid_subjectconfirmation_noa.errors, "A valid SubjectConfirmation was not found on this Response"
       end
+
+      it "return true when the skip_subject_confirmation option is passed and the subject confirmation is valid" do
+        opts = {}
+        opts[:skip_subject_confirmation] = true
+        response_with_skip = OneLogin::RubySaml::Response.new(response_document_valid_signed, opts)
+        response_with_skip.settings = settings
+        response_with_skip.settings.assertion_consumer_service_url = 'recipient'
+        Time.expects(:now).times(0) # ensures the test isn't run and thus Time.now.utc is never called within the test
+        assert response_with_skip.send(:validate_subject_confirmation)
+        assert_empty response_with_skip.errors
+      end
+
+      it "return true when the skip_subject_confirmation option is passed and the response has an invalid subject confirmation" do
+        opts = {}
+        opts[:skip_subject_confirmation] = true
+        response_with_skip = OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_noa.xml.base64"), opts)
+        response_with_skip.settings = settings
+        Time.expects(:now).times(0) # ensures the test isn't run and thus Time.now.utc is never called within the test
+        assert response_with_skip.send(:validate_subject_confirmation)
+        assert_empty response_with_skip.errors
+      end
     end
 
     describe "#validate_session_expiration" do
@@ -620,6 +668,18 @@ class RubySamlTest < Minitest::Test
         assert !response.send(:validate_session_expiration)
         assert_includes response.errors, "The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response"
       end
+      
+      it "returns true when the session has expired, but is still within the allowed_clock_drift" do
+        drift = (Time.now - Time.parse("2010-11-19T21:57:37Z")) * 60 # seconds ago that this assertion expired
+        drift += 10 # add a buffer of 10 seconds to make sure the test passes
+        opts = {}
+        opts[:allowed_clock_drift] = drift
+
+        response_with_drift = OneLogin::RubySaml::Response.new(response_document_without_recipient, opts)
+        response_with_drift.settings = settings
+        assert response_with_drift.send(:validate_session_expiration)
+        assert_empty response_with_drift.errors
+      end
     end
 
     describe "#validate_signature" do
@@ -630,6 +690,22 @@ class RubySamlTest < Minitest::Test
         assert_empty response_valid_signed.errors
       end
 
+      it "return true when the signature is valid and ds namespace is at the root" do
+        settings.idp_cert_fingerprint = '5614657ab692b960480389723a36446a5fe1f7ec'
+        response_with_ds_namespace_at_the_root.settings = settings
+        assert response_with_ds_namespace_at_the_root.send(:validate_signature)
+        assert_empty response_with_ds_namespace_at_the_root.errors
+      end
+
+      it "return true when the signature is valid and fingerprint provided" do
+        settings.idp_cert_fingerprint = '49:EC:3F:A4:71:8A:1E:C9:DB:70:A7:CC:33:36:96:F0:48:8C:4E:DA'
+        xml = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwczovL2NvZGVycGFkLmlvL3NhbWwvYWNzIiBJRD0iXzEwOGE1ZTg0MDllYzRjZjlhY2QxYzQ2OWU5ZDcxNGFkIiBJblJlc3BvbnNlVG89Il80ZmZmYWE2MC02OTZiLTAxMzMtMzg4Ni0wMjQxZjY1YzA2OTMiIElzc3VlSW5zdGFudD0iMjAxNS0xMS0wOVQyMzo1NTo0M1oiIFZlcnNpb249IjIuMCI+PHNhbWw6SXNzdWVyIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHBzOi8vbG9naW4uaHVsdS5jb208L3NhbWw6SXNzdWVyPjxkczpTa [...]
+        response_x = OneLogin::RubySaml::Response.new(xml)
+        response_x.settings = settings
+        assert response_x.send(:validate_signature)
+        assert_empty response_x.errors
+      end
+
       it "return false when no fingerprint" do
         settings.idp_cert_fingerprint = nil
         settings.idp_cert = nil
@@ -644,6 +720,41 @@ class RubySamlTest < Minitest::Test
         assert !response.send(:validate_signature)
         assert_includes response.errors, "Invalid Signature on SAML Response"
       end
+
+      it "return false when no X509Certificate and not cert provided at settings" do
+        settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
+        settings.idp_cert = nil
+        response_valid_signed_without_x509certificate.settings = settings
+        assert !response_valid_signed_without_x509certificate.send(:validate_signature)
+        assert_includes response_valid_signed_without_x509certificate.errors, "Invalid Signature on SAML Response"
+      end
+
+      it "return false when no X509Certificate and the cert provided at settings mismatches" do
+        settings.idp_cert_fingerprint = nil
+        settings.idp_cert = signature_1
+        response_valid_signed_without_x509certificate.settings = settings
+        assert !response_valid_signed_without_x509certificate.send(:validate_signature)
+        assert_includes response_valid_signed_without_x509certificate.errors, "Invalid Signature on SAML Response"        
+      end
+
+      it "return true when no X509Certificate and the cert provided at settings matches" do
+        settings.idp_cert_fingerprint = nil
+        settings.idp_cert = ruby_saml_cert_text
+        response_valid_signed_without_x509certificate.settings = settings
+        assert response_valid_signed_without_x509certificate.send(:validate_signature)
+        assert_empty response_valid_signed_without_x509certificate.errors
+      end
+
+      it "return false when signature wrapping attack" do
+        signature_wrapping_attack = read_invalid_response("signature_wrapping_attack.xml.base64")
+        response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack)
+        response_wrapped.stubs(:conditions).returns(nil)
+        response_wrapped.stubs(:validate_subject_confirmation).returns(true)
+        settings.idp_cert_fingerprint = "afe71c28ef740bc87425be13a2263d37971da1f9"
+        response_wrapped.settings = settings
+        assert !response_wrapped.send(:validate_signature)
+        assert_includes response_wrapped.errors, "Invalid Signature on SAML Response"
+       end
     end
 
     describe "#nameid" do
@@ -888,7 +999,7 @@ class RubySamlTest < Minitest::Test
       it 'is not possible when encryptID inside the assertion but no private key' do
           response_encrypted_nameid.settings = settings
           assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedID found and no SP private key found on the settings to decrypt it") do
-            assert_equal "test at onelogin.com", response_encrypted_nameid.nameid    
+            assert_equal "test at onelogin.com", response_encrypted_nameid.nameid
           end
       end
 
@@ -1007,7 +1118,7 @@ class RubySamlTest < Minitest::Test
     describe "check right settings" do
 
       it "is not possible to decrypt the assertion if no private key" do
-        response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)        
+        response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
 
         encrypted_assertion_node = REXML::XPath.first(
           response.document,
@@ -1023,7 +1134,7 @@ class RubySamlTest < Minitest::Test
       end
 
       it "is possible to decrypt the assertion if private key" do
-        response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)        
+        response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
 
         encrypted_assertion_node = REXML::XPath.first(
           response.document,
@@ -1090,5 +1201,27 @@ class RubySamlTest < Minitest::Test
         assert_equal "_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7", response.nameid
       end
     end
+
+  end
+  describe "test qualified name id in attributes" do
+
+    it "parsed the nameid" do
+     response = OneLogin::RubySaml::Response.new(read_response("signed_nameid_in_atts.xml"), :settings => settings)
+     response.settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
+     assert_empty response.errors
+     assert_equal "test", response.attributes[:uid]
+     assert_equal "http://idp.example.com/metadata.php/ZdrjpwEdw22vKoxWAbZB78/gQ7s=", response.attributes.single('urn:oid:1.3.6.1.4.1.5923.1.1.1.10')
+    end
+  end
+
+  describe "test unqualified name id in attributes" do
+
+    it "parsed the nameid" do
+     response = OneLogin::RubySaml::Response.new(read_response("signed_unqual_nameid_in_atts.xml"), :settings => settings)
+     response.settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
+     assert_empty response.errors
+     assert_equal "test", response.attributes[:uid]
+     assert_equal "ZdrjpwEdw22vKoxWAbZB78/gQ7s=", response.attributes.single('urn:oid:1.3.6.1.4.1.5923.1.1.1.10')
+    end
   end
 end
diff --git a/test/responses/invalids/signature_wrapping_attack.xml.base64 b/test/responses/invalids/signature_wrapping_attack.xml.base64
new file mode 100644
index 0000000..dc2c9ca
--- /dev/null
+++ b/test/responses/invalids/signature_wrapping_attack.xml.base64
@@ -0,0 +1 @@
+PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJwZnhjM2QyYjU0Mi0wZjdlLTg3NjctOGU4Ny01YjBkYzY5MTMzNzUiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE0LTAzLTIxVDEzOjQxOjA5WiIgRGVzdGluYXRpb249Imh0dHBzOi8vcGl0YnVsay5uby1pcC5vcmcvbmV3b25lbG9naW4vZGVtbzEvaW5kZXgucGhwP2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl81ZDllMzE5YzFiOGE2N2RhNDgyMjc5NjRjMjhkMjgwZTc4NjBmODA0Ij48c2FtbDpJc3N1ZXI+aHR0cHM6 [...]
\ No newline at end of file
diff --git a/test/responses/response_with_ds_namespace_at_the_root.xml.base64 b/test/responses/response_with_ds_namespace_at_the_root.xml.base64
new file mode 100644
index 0000000..48893cd
--- /dev/null
+++ b/test/responses/response_with_ds_namespace_at_the_root.xml.base64
@@ -0,0 +1 @@
+PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiDQp4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgSUQ9Il84ZThkYzVmNjlhOThjYzRjMWZmMzQyN2U1Y2UzNDYwNmZkNjcyZjkxZTYiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE0LTA3LTE3VDAxOjAxOjQ4WiIgRGVzdGluYXRpb249Imh0dHA6Ly9zcC5leGFtcGxlLmNvbS9kZW1vMS9pbmRleC5waHA/YWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzRmZWUzYjA0NjM5NWM0ZTc1MTAxMWU5N2Y4 [...]
\ No newline at end of file
diff --git a/test/responses/response_with_signed_message_and_assertion.xml b/test/responses/response_with_signed_message_and_assertion.xml
new file mode 100644
index 0000000..a5bca26
--- /dev/null
+++ b/test/responses/response_with_signed_message_and_assertion.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="pfx0a3cfa31-f178-71f2-9b94-ad4047591acc" Version="2.0" IssueInstant="2012-04-04T07:33:10.921Z" Destination="https://example.com/endpoint">
+  <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">idp.example.com</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+  <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+  <ds:Reference URI="#pfx0a3cfa31-f178-71f2-9b94-ad4047591acc"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>hi2Ouec0ovl90Cz+OXAP6FD5X70=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>tJiaa5aZNzLFbBiIsyc0MBI4G1caG+gOW0joGlbMAyY86ERaDwDi1sz98+vykZOgjwkfZL [...]
+<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICGzCCAYQCCQCNNcQXom32VDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSU4xFTATBgNVBAcTDEluZGlhbmFwb2xpczERMA8GA1UEChMIT25lTG9naW4xDDAKBgNVBAsTA0VuZzAeFw0xNDA0MjMxODQxMDFaFw0xNTA0MjMxODQxMDFaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTjEVMBMGA1UEBxMMSW5kaWFuYXBvbGlzMREwDwYDVQQKEwhPbmVMb2dpbjEMMAoGA1UECxMDRW5nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo6m+QZvYQ/xL0ElLgupK1QDcYL4f5PckwsNgS9pUvV7fzTqCHk8ThLxTk42MQ2McJsOeUJVP728KhymjFCqxgP4VuwRk9rpA [...]
+  <samlp:Status>
+    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
+  </samlp:Status>
+  <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" IssueInstant="2012-04-04T07:33:10.923Z" ID="pfx7fca52d6-8991-5d99-3147-4f9d7c278d78">
+    <saml:Issuer>idp.myexample.org</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+  <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+  <ds:Reference URI="#pfx7fca52d6-8991-5d99-3147-4f9d7c278d78"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>FA0AbR4w9oYdx7MFjERARVJAHps=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>GDH5jhCNX9PFxW+71SOJPyusAOwzECwmd57NDhvA/VKWHnV3PpvpNkOLyamoBNdZ4qxpon [...]
+<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICGzCCAYQCCQCNNcQXom32VDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSU4xFTATBgNVBAcTDEluZGlhbmFwb2xpczERMA8GA1UEChMIT25lTG9naW4xDDAKBgNVBAsTA0VuZzAeFw0xNDA0MjMxODQxMDFaFw0xNTA0MjMxODQxMDFaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTjEVMBMGA1UEBxMMSW5kaWFuYXBvbGlzMREwDwYDVQQKEwhPbmVMb2dpbjEMMAoGA1UECxMDRW5nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo6m+QZvYQ/xL0ElLgupK1QDcYL4f5PckwsNgS9pUvV7fzTqCHk8ThLxTk42MQ2McJsOeUJVP728KhymjFCqxgP4VuwRk9rpA [...]
+    <saml:Subject>
+      <saml:NameID NameQualifier="idp.example.com" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">someone at example.org</saml:NameID>
+      <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
+        <saml:SubjectConfirmationData Recipient="https://example.com/endpoint" InResponseTo="_f7201940-6055-012f-3bc1-782bcb13c426"/>
+      </saml:SubjectConfirmation>
+    </saml:Subject>
+    <saml:Conditions NotBefore="2012-04-04T07:28:11.442Z" NotOnOrAfter="2012-04-04T07:38:11.442Z">
+      <saml:AudienceRestriction>
+        <saml:Audience>example.com</saml:Audience>
+      </saml:AudienceRestriction>
+    </saml:Conditions>
+    <saml:AuthnStatement AuthnInstant="2012-04-04T07:33:11.442Z">
+      <saml:AuthnContext>
+        <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
+      </saml:AuthnContext>
+    </saml:AuthnStatement>
+  </saml:Assertion>
+</samlp:Response>
\ No newline at end of file
diff --git a/test/responses/response_without_reference_uri.xml.base64 b/test/responses/response_without_reference_uri.xml.base64
new file mode 100644
index 0000000..dd5f7b5
--- /dev/null
+++ b/test/responses/response_without_reference_uri.xml.base64
@@ -0,0 +1 @@
+PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9InBmeGQ1OTQzNDdkLTQ5NWYtYjhkMS0wZWUyLTQxY2ZkYTE0ZGQzNSIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTUtMDEtMDJUMjI6NDg6NDhaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2xvY2FsaG9zdDo5MDAxL3YxL3VzZXJzL2F1dGhvcml6ZS9zYW1sIiBDb25zZW50PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y29uc2VudDp1bnNwZWNpZmllZCIgSW5SZXNwb25zZVRvPSJfZWQ5MTVhNDAtNzRmYi0wMTMyLTViMTYtNDhlMGViMTRhMWM3Ij4NCiAgPElzc3VlciB4 [...]
diff --git a/test/responses/signed_nameid_in_atts.xml b/test/responses/signed_nameid_in_atts.xml
new file mode 100644
index 0000000..5e7ec59
--- /dev/null
+++ b/test/responses/signed_nameid_in_atts.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="pfx3437f097-cd95-f187-4501-0bce24d4d6f8" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685">
+    <saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+  <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+  <ds:Reference URI="#pfx3437f097-cd95-f187-4501-0bce24d4d6f8"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>7IMOicPUD8hBuAv8HeVDrUErclY=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>jPBUr/iVsSXvMeT6PhQxSTbi3j6M34OQiyAKLPyPQWypX0uJ04UbC2J4v1DqFqC3OJYyep [...]
+<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICGzCCAYQCCQCNNcQXom32VDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSU4xFTATBgNVBAcTDEluZGlhbmFwb2xpczERMA8GA1UEChMIT25lTG9naW4xDDAKBgNVBAsTA0VuZzAeFw0xNDA0MjMxODQxMDFaFw0xNTA0MjMxODQxMDFaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTjEVMBMGA1UEBxMMSW5kaWFuYXBvbGlzMREwDwYDVQQKEwhPbmVMb2dpbjEMMAoGA1UECxMDRW5nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo6m+QZvYQ/xL0ElLgupK1QDcYL4f5PckwsNgS9pUvV7fzTqCHk8ThLxTk42MQ2McJsOeUJVP728KhymjFCqxgP4VuwRk9rpA [...]
+    <samlp:Status>
+        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
+    </samlp:Status>
+    <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_d71a3a8e9fcc45c9e9d248ef7049393fc8f04e5f75" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
+        <saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
+        <saml:Subject>
+            <saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID>
+            <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
+                <saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
+            </saml:SubjectConfirmation>
+        </saml:Subject>
+        <saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
+            <saml:AudienceRestriction>
+                <saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
+            </saml:AudienceRestriction>
+        </saml:Conditions>
+        <saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
+            <saml:AuthnContext>
+                <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
+            </saml:AuthnContext>
+        </saml:AuthnStatement>
+        <saml:AttributeStatement>
+            <saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+                <saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
+            </saml:Attribute>
+            <saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+                <saml:AttributeValue xsi:type="xs:string">test at example.com</saml:AttributeValue>
+            </saml:Attribute>
+            <saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+                <saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
+                <saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
+            </saml:Attribute>
+            <saml:Attribute FriendlyName="eduPersonTargetedID" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+                <saml:AttributeValue>
+                    <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameQualifier="http://idp.example.com/metadata.php" SPNameQualifier="http://sp.example.com/demo1/metadata.php">ZdrjpwEdw22vKoxWAbZB78/gQ7s=</saml:NameID>
+                </saml:AttributeValue>
+            </saml:Attribute>
+        </saml:AttributeStatement>
+    </saml:Assertion>
+</samlp:Response>
diff --git a/test/responses/signed_unqual_nameid_in_atts.xml b/test/responses/signed_unqual_nameid_in_atts.xml
new file mode 100644
index 0000000..66a0beb
--- /dev/null
+++ b/test/responses/signed_unqual_nameid_in_atts.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="pfx852f7331-dd1c-95c3-9cbe-0ab1ebf9c538" Version="2.0" IssueInstant="2014-07-17T01:01:48Z" Destination="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685">
+    <saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+  <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+  <ds:Reference URI="#pfx852f7331-dd1c-95c3-9cbe-0ab1ebf9c538"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>LQa/IrEdbLE1BHEP3B7KfvOABpg=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>fWg7OauBy7cExPo+GbNVb1e1OopaYc0ke+BYF1N4bpoej86o7U75xOttDN7oz58LhpDZYW [...]
+<ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICGzCCAYQCCQCNNcQXom32VDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSU4xFTATBgNVBAcTDEluZGlhbmFwb2xpczERMA8GA1UEChMIT25lTG9naW4xDDAKBgNVBAsTA0VuZzAeFw0xNDA0MjMxODQxMDFaFw0xNTA0MjMxODQxMDFaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTjEVMBMGA1UEBxMMSW5kaWFuYXBvbGlzMREwDwYDVQQKEwhPbmVMb2dpbjEMMAoGA1UECxMDRW5nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo6m+QZvYQ/xL0ElLgupK1QDcYL4f5PckwsNgS9pUvV7fzTqCHk8ThLxTk42MQ2McJsOeUJVP728KhymjFCqxgP4VuwRk9rpA [...]
+    <samlp:Status>
+        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
+    </samlp:Status>
+    <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_d71a3a8e9fcc45c9e9d248ef7049393fc8f04e5f75" Version="2.0" IssueInstant="2014-07-17T01:01:48Z">
+        <saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>
+        <saml:Subject>
+            <saml:NameID SPNameQualifier="http://sp.example.com/demo1/metadata.php" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID>
+            <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
+                <saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://sp.example.com/demo1/index.php?acs" InResponseTo="ONELOGIN_4fee3b046395c4e751011e97f8900b5273d56685"/>
+            </saml:SubjectConfirmation>
+        </saml:Subject>
+        <saml:Conditions NotBefore="2014-07-17T01:01:18Z" NotOnOrAfter="2024-01-18T06:21:48Z">
+            <saml:AudienceRestriction>
+                <saml:Audience>http://sp.example.com/demo1/metadata.php</saml:Audience>
+            </saml:AudienceRestriction>
+        </saml:Conditions>
+        <saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93">
+            <saml:AuthnContext>
+                <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
+            </saml:AuthnContext>
+        </saml:AuthnStatement>
+        <saml:AttributeStatement>
+            <saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+                <saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
+            </saml:Attribute>
+            <saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+                <saml:AttributeValue xsi:type="xs:string">test at example.com</saml:AttributeValue>
+            </saml:Attribute>
+            <saml:Attribute Name="eduPersonAffiliation" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
+                <saml:AttributeValue xsi:type="xs:string">users</saml:AttributeValue>
+                <saml:AttributeValue xsi:type="xs:string">examplerole1</saml:AttributeValue>
+            </saml:Attribute>
+            <saml:Attribute FriendlyName="eduPersonTargetedID" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+                <saml:AttributeValue>
+                    <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">ZdrjpwEdw22vKoxWAbZB78/gQ7s=</saml:NameID>
+                </saml:AttributeValue>
+            </saml:Attribute>
+        </saml:AttributeStatement>
+    </saml:Assertion>
+</samlp:Response>
diff --git a/test/responses/valid_response_without_x509certificate.xml.base64 b/test/responses/valid_response_without_x509certificate.xml.base64
new file mode 100644
index 0000000..55f5139
--- /dev/null
+++ b/test/responses/valid_response_without_x509certificate.xml.base64
@@ -0,0 +1 @@
+pVZdd9o4EH3fc/Y/+LiPOcayDTb4BLoU0oSWfBDTbpuXPbI0Bie25Egi0Pz6lQ04kJI03X2CGY/u3LkjjXT8fpVnxgMImXLWNZ0GMt/3/vzjWOI8K8JrkAVnEgwdxGRYOrvmQrCQY5nKkOEcZKhIGPXPx6HbQCGWEoTSUObOkuL1NYXgihOemcZo2DWLZBWTtuvjhFrQScCiXhJbtB00LdIMECIeJIi0TePrlrPG0EulXMCISYWZ0i7kNC3kW6g5RW7ouiFyb0xjCFKlDKtq1VypIrRtXBSNfEFxg3FbSm4TXe4iBw3ItsVPedf8JyFN7DVjZAWg6SDHBYtgDFbQdmMSO14ce22zV8kWVlxEr8wgNyk4g4zPUtYgPLfLIPfY3o09pjKM0pkmtxBbtamsWS6Xy8bSa3Axs12EkI06to6hMp29M3W3DGO7HuiIJbyCG2DGWUpwlj5WJZ+DmnNq9LMZF6ma5y+AO7aDSnALVsQi [...]
\ No newline at end of file
diff --git a/test/test_helper.rb b/test/test_helper.rb
index c6488f1..9c36685 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -10,6 +10,7 @@ require 'rubygems'
 require 'bundler'
 require 'minitest/autorun'
 require 'mocha/setup'
+require 'timecop'
 
 Bundler.require :default, :test
 
@@ -51,6 +52,10 @@ class Minitest::Test
     @response_document_valid_signed ||= read_response("valid_response.xml.base64")
   end
 
+  def response_document_valid_signed_without_x509certificate
+    @response_document_valid_signed_without_x509certificate ||= read_response("valid_response_without_x509certificate.xml.base64")
+  end
+
   def response_document_without_recipient
     @response_document_without_recipient ||= read_response("response_with_undefined_recipient.xml.base64")
   end
@@ -66,6 +71,10 @@ class Minitest::Test
     @response_document_without_attributes ||= read_response("response_without_attributes.xml.base64")
   end
 
+  def response_document_without_reference_uri
+    @response_document_without_reference_uri ||= read_response("response_without_reference_uri.xml.base64")
+  end
+
   def response_document_with_signed_assertion
     @response_document_with_signed_assertion ||= read_response("response_with_signed_assertion.xml.base64")
   end
@@ -74,6 +83,10 @@ class Minitest::Test
     @response_document_with_signed_assertion_2 ||= read_response("response_with_signed_assertion_2.xml.base64")
   end
 
+  def response_document_with_ds_namespace_at_the_root
+    @response_document_with_ds_namespace_at_the_root ||= read_response("response_with_ds_namespace_at_the_root.xml.base64")
+  end
+
   def response_document_unsigned
     @response_document_unsigned ||= read_response("response_unsigned_xml_base64")
   end
diff --git a/test/xml_security_test.rb b/test/xml_security_test.rb
index 0bf4ccf..fb54077 100644
--- a/test/xml_security_test.rb
+++ b/test/xml_security_test.rb
@@ -1,6 +1,5 @@
 require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
 require 'xml_security'
-require 'timecop'
 
 class XmlSecurityTest < Minitest::Test
   include XMLSecurity
@@ -69,13 +68,20 @@ class XmlSecurityTest < Minitest::Test
       assert adfs_document.validate_signature(base64cert, false)
     end
 
-    it "raise validation error when the X509Certificate is missing" do
+    it "raise validation error when the X509Certificate is missing and no cert provided" do
       decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
       mod_document = XMLSecurity::SignedDocument.new(decoded_response)
       exception = assert_raises(OneLogin::RubySaml::ValidationError) do
         mod_document.validate_document("a fingerprint", false) # The fingerprint isn't relevant to this test
       end
-      assert_equal("Certificate element missing in response (ds:X509Certificate)", exception.message)
+      assert_equal("Certificate element missing in response (ds:X509Certificate) and not cert provided at settings", exception.message)
+    end
+
+    it "invalidaties when the X509Certificate is missing and the cert is provided but mismatches" do
+      decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
+      mod_document = XMLSecurity::SignedDocument.new(decoded_response)
+      cert = OpenSSL::X509::Certificate.new(ruby_saml_cert)
+      assert !mod_document.validate_document("a fingerprint", true, :cert => cert) # The fingerprint isn't relevant to this test
     end
   end
 
@@ -215,14 +221,14 @@ class XmlSecurityTest < Minitest::Test
         assert response.is_valid?
       end
 
-      it "return an empty list when inclusive namespace element is missing" do
+      it "return nil when inclusive namespace element is missing" do
         response = fixture(:no_signature_ns, false)
         response.slice! %r{<InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="#default saml ds xs xsi"/>}
 
         document = XMLSecurity::SignedDocument.new(response)
         inclusive_namespaces = document.send(:extract_inclusive_namespaces)
 
-        assert inclusive_namespaces.empty?
+        assert inclusive_namespaces.nil?
       end
     end
 
@@ -234,8 +240,7 @@ class XmlSecurityTest < Minitest::Test
         settings.issuer = "https://sp.example.com/saml2"
         settings.assertion_consumer_service_url = "https://sp.example.com/acs"
         settings.single_logout_service_url = "https://sp.example.com/sls"
-      end 
-
+      end
 
       it "sign an AuthNRequest" do
         request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
@@ -325,5 +330,28 @@ class XmlSecurityTest < Minitest::Test
         end
       end
     end
+
+    describe '#validate_document' do
+      describe 'with valid document' do
+        describe 'when response has signed message and assertion' do
+          let(:document_data) { read_response('response_with_signed_message_and_assertion.xml') }
+          let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
+          let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
+
+          it 'is valid' do
+            assert document.validate_document(fingerprint, true), 'Document should be valid'
+          end
+        end
+      end
+      describe 'signature_wrapping_attack' do
+        let(:document_data) { read_invalid_response("signature_wrapping_attack.xml.base64") }
+        let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
+        let(:fingerprint) { 'afe71c28ef740bc87425be13a2263d37971da1f9' }
+
+        it 'is invalid' do
+          assert !document.validate_document(fingerprint, true), 'Document should be invalid'
+        end
+      end
+    end
   end
 end

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-saml.git



More information about the Pkg-ruby-extras-commits mailing list