Comparing changes

v0.3.0 v0.3.1
28 commits 67 files changed

Commits

322cd35 bump version. mo 2017-12-28 19:38:54
c96388a install xml-kit from rubygems. mo 2017-12-28 19:23:42
86655b6 drop Xml prefix. mo 2017-12-28 19:04:37
66fe4a7 move dependencies to xml-kit. mo 2017-12-28 18:57:40
b8b523d shorten let. mo 2017-12-28 18:57:14
0a58f09 use id generator. mo 2017-12-28 18:56:54
ff7ed36 fix exes mo 2017-12-28 18:56:36
99fe835 extract attr_accessor. mo 2017-12-28 18:34:51
255864c assign assertion to ivar. mo 2017-12-28 17:58:27
4fb61ae rename Xml -> Document. mo 2017-12-28 17:41:17
2705813 extract variable. mo 2017-12-28 17:30:56
015370c include saml-kit to fix specs. mo 2017-12-26 23:13:48
a2d1b3a allow namespaces parameter. mo 2017-12-26 21:53:00
b44eeff move xml namespaces to xml-kit. mo 2017-12-26 21:42:08
eecdfdf move xml namespaces to xml-kit. mo 2017-12-26 21:30:08
298458f connect xml-kit logger. mo 2017-12-26 21:18:07
ec448d9 remove unused dependency. mo 2017-12-26 21:03:49
7101c05 fix specs in xml-kit. mo 2017-12-26 21:03:15
3389355 move xml decryption to xml-kit. mo 2017-12-26 20:43:31
0bd09e0 create xml-kit. mo 2017-12-26 20:32:47
exe/saml-kit-create-self-signed-certificate
@@ -3,7 +3,7 @@ require 'saml/kit'
 
 puts "Enter Passphrase:"
 passphrase = STDIN.read.strip
-certificate, private_key = Saml::Kit::SelfSignedCertificate.new(passphrase).create
+certificate, private_key = ::Xml::Kit::SelfSignedCertificate.new(passphrase).create
 
 puts "** BEGIN File Format **"
 print certificate
exe/saml-kit-decode-http-post
@@ -2,7 +2,5 @@
 require 'saml/kit'
 
 saml = STDIN.read
-
 binding = Saml::Kit::Bindings::HttpPost.new(location: '')
-xml = binding.deserialize('SAMLRequest' => saml).to_xml
-puts Nokogiri::XML(xml).to_xml(indent: 2)
+puts binding.deserialize('SAMLRequest' => saml).to_xml(pretty: true)
exe/saml-kit-decode-http-redirect
@@ -2,6 +2,5 @@
 require 'saml/kit'
 
 saml = STDIN.read
-binding = Saml::Kit::HttpRedirectBinding.new(location: '')
-xml = binding.deserialize('SAMLRequest' => saml).to_xml
-puts Nokogiri::XML(xml).to_xml(indent: 2)
+binding = Saml::Kit::Bindings::HttpRedirectBinding.new(location: '')
+puts binding.deserialize('SAMLRequest' => saml).to_xml(pretty: true)
lib/saml/kit/bindings/url_builder.rb
@@ -34,7 +34,7 @@ module Saml
           to_query_string(
             saml_document.query_string_parameter => serialize(saml_document.to_xml),
             'RelayState' => relay_state,
-            'SigAlg' => Saml::Kit::Namespaces::SHA256,
+            'SigAlg' => ::Xml::Kit::Namespaces::SHA256,
           )
         end
 
lib/saml/kit/builders/templates/assertion.builder
@@ -1,29 +1,27 @@
-encryption_for(xml: xml) do |xml|
-  xml.Assertion(assertion_options) do
-    xml.Issuer issuer
-    signature_for(reference_id: reference_id, xml: xml)
-    xml.Subject do
-      xml.NameID name_id, Format: name_id_format
-      xml.SubjectConfirmation Method: Saml::Kit::Namespaces::BEARER do
-        xml.SubjectConfirmationData "", subject_confirmation_data_options
-      end
+xml.Assertion(assertion_options) do
+  xml.Issuer issuer
+  signature_for(reference_id: reference_id, xml: xml)
+  xml.Subject do
+    xml.NameID name_id, Format: name_id_format
+    xml.SubjectConfirmation Method: Saml::Kit::Namespaces::BEARER do
+      xml.SubjectConfirmationData "", subject_confirmation_data_options
     end
-    xml.Conditions conditions_options do
-      xml.AudienceRestriction do
-        xml.Audience request.issuer
-      end
+  end
+  xml.Conditions conditions_options do
+    xml.AudienceRestriction do
+      xml.Audience request.issuer
     end
-    xml.AuthnStatement authn_statement_options do
-      xml.AuthnContext do
-        xml.AuthnContextClassRef Saml::Kit::Namespaces::PASSWORD
-      end
+  end
+  xml.AuthnStatement authn_statement_options do
+    xml.AuthnContext do
+      xml.AuthnContextClassRef Saml::Kit::Namespaces::PASSWORD
     end
-    if assertion_attributes.any?
-      xml.AttributeStatement do
-        assertion_attributes.each do |key, value|
-          xml.Attribute Name: key, NameFormat: Saml::Kit::Namespaces::URI, FriendlyName: key do
-            xml.AttributeValue value.to_s
-          end
+  end
+  if assertion_attributes.any?
+    xml.AttributeStatement do
+      assertion_attributes.each do |key, value|
+        xml.Attribute Name: key, NameFormat: Saml::Kit::Namespaces::URI, FriendlyName: key do
+          xml.AttributeValue value.to_s
         end
       end
     end
lib/saml/kit/builders/templates/certificate.builder
@@ -1,7 +0,0 @@
-xml.KeyDescriptor use: use do
-  xml.KeyInfo "xmlns": Saml::Kit::Namespaces::XMLDSIG do
-    xml.X509Data do
-      xml.X509Certificate stripped
-    end
-  end
-end
lib/saml/kit/builders/templates/encrypted_assertion.builder
@@ -0,0 +1,5 @@
+xml.EncryptedAssertion xmlns: Saml::Kit::Namespaces::ASSERTION do
+  encryption_for(xml: xml) do |xml|
+    render assertion, xml: xml
+  end
+end
lib/saml/kit/builders/templates/nil_class.builder
lib/saml/kit/builders/templates/xml_encryption.builder
@@ -1,16 +0,0 @@
-xml.EncryptedAssertion xmlns: Saml::Kit::Namespaces::ASSERTION do
-  xml.EncryptedData xmlns: Saml::Kit::Namespaces::XMLENC do
-    xml.EncryptionMethod Algorithm: "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
-    xml.KeyInfo xmlns: Saml::Kit::Namespaces::XMLDSIG do
-      xml.EncryptedKey xmlns: Saml::Kit::Namespaces::XMLENC do
-        xml.EncryptionMethod Algorithm: "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
-        xml.CipherData do
-          xml.CipherValue Base64.encode64(public_key.public_encrypt(key))
-        end
-      end
-    end
-    xml.CipherData do
-      xml.CipherValue Base64.encode64(iv + encrypted)
-    end
-  end
-end
lib/saml/kit/builders/templates/xml_signature.builder
@@ -1,20 +0,0 @@
-xml.Signature "xmlns" => Saml::Kit::Namespaces::XMLDSIG do
-  xml.SignedInfo do
-    xml.CanonicalizationMethod Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#"
-    xml.SignatureMethod Algorithm: signature_method
-    xml.Reference URI: "##{reference_id}" do
-      xml.Transforms do
-        xml.Transform Algorithm: "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
-        xml.Transform Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#"
-      end
-      xml.DigestMethod Algorithm: digest_method
-      xml.DigestValue ""
-    end
-  end
-  xml.SignatureValue ""
-  xml.KeyInfo do
-    xml.X509Data do
-      xml.X509Certificate certificate.stripped
-    end
-  end
-end
lib/saml/kit/builders/assertion.rb
@@ -1,11 +1,12 @@
 module Saml
   module Kit
     module Builders
+      # {include:file:lib/saml/kit/builders/templates/assertion.builder}
       class Assertion
-        include Templatable
+        include XmlTemplatable
         extend Forwardable
 
-        def_delegators :@response_builder, :encrypt, :embed_signature, :request, :issuer, :reference_id, :now, :configuration, :user, :version, :destination, :encryption_certificate
+        def_delegators :@response_builder, :embed_signature, :request, :issuer, :reference_id, :now, :configuration, :user, :version, :destination
 
         def initialize(response_builder)
           @response_builder = response_builder
lib/saml/kit/builders/authentication_request.rb
@@ -1,16 +1,17 @@
 module Saml
   module Kit
     module Builders
+      # {include:file:lib/saml/kit/builders/templates/authentication_request.builder}
       # {include:file:spec/saml/builders/authentication_request_spec.rb}
       class AuthenticationRequest
-        include Saml::Kit::Templatable
+        include XmlTemplatable
         attr_accessor :id, :now, :issuer, :assertion_consumer_service_url, :name_id_format, :destination
         attr_accessor :version
         attr_reader :configuration
 
         def initialize(configuration: Saml::Kit.configuration)
           @configuration = configuration
-          @id = Id.generate
+          @id = ::Xml::Kit::Id.generate
           @issuer = configuration.issuer
           @name_id_format = Namespaces::PERSISTENT
           @now = Time.now.utc
lib/saml/kit/builders/encrypted_assertion.rb
@@ -0,0 +1,20 @@
+module Saml
+  module Kit
+    module Builders
+      # {include:file:lib/saml/kit/builders/templates/encrypted_assertion.builder}
+      class EncryptedAssertion
+        include XmlTemplatable
+        extend Forwardable
+
+        attr_reader :assertion
+        def_delegators :@response_builder, :configuration, :encryption_certificate
+
+        def initialize(response_builder, assertion)
+          @response_builder = response_builder
+          @assertion = assertion
+          @encrypt = true
+        end
+      end
+    end
+  end
+end
lib/saml/kit/builders/identity_provider_metadata.rb
@@ -1,9 +1,10 @@
 module Saml
   module Kit
     module Builders
+      # {include:file:lib/saml/kit/builders/templates/identity_provider_metadata.builder}
       # {include:file:spec/saml/builders/identity_provider_metadata_spec.rb}
       class IdentityProviderMetadata
-        include Saml::Kit::Templatable
+        include XmlTemplatable
         extend Forwardable
         attr_accessor :attributes, :name_id_formats
         attr_accessor :want_authn_requests_signed
@@ -16,7 +17,7 @@ module Saml
           @attributes = []
           @configuration = configuration
           @entity_id = configuration.issuer
-          @id = Id.generate
+          @id = ::Xml::Kit::Id.generate
           @logout_urls = []
           @name_id_formats = [Namespaces::PERSISTENT]
           @single_sign_on_urls = []
@@ -42,7 +43,7 @@ module Saml
         def entity_descriptor_options
           {
             'xmlns': Namespaces::METADATA,
-            'xmlns:ds': Namespaces::XMLDSIG,
+            'xmlns:ds': ::Xml::Kit::Namespaces::XMLDSIG,
             'xmlns:saml': Namespaces::ASSERTION,
             ID: id,
             entityID: entity_id,
lib/saml/kit/builders/logout_request.rb
@@ -1,9 +1,10 @@
 module Saml
   module Kit
     module Builders
+      # {include:file:lib/saml/kit/builders/templates/logout_request.builder}
       # {include:file:spec/saml/builders/logout_request_spec.rb}
       class LogoutRequest
-        include Saml::Kit::Templatable
+        include XmlTemplatable
         attr_accessor :id, :destination, :issuer, :name_id_format, :now
         attr_accessor :version
         attr_reader :user, :configuration
@@ -11,7 +12,7 @@ module Saml
         def initialize(user, configuration: Saml::Kit.configuration)
           @configuration = configuration
           @user = user
-          @id = "_#{SecureRandom.uuid}"
+          @id = ::Xml::Kit::Id.generate
           @issuer = configuration.issuer
           @name_id_format = Saml::Kit::Namespaces::PERSISTENT
           @now = Time.now.utc
lib/saml/kit/builders/logout_response.rb
@@ -1,16 +1,17 @@
 module Saml
   module Kit
     module Builders
+      # {include:file:lib/saml/kit/builders/templates/logout_response.builder}
       # {include:file:spec/saml/builders/logout_response_spec.rb}
       class LogoutResponse
-        include Saml::Kit::Templatable
+        include XmlTemplatable
         attr_accessor :id, :issuer, :version, :status_code, :now, :destination
         attr_reader :request
         attr_reader :configuration
 
         def initialize(request, configuration: Saml::Kit.configuration)
           @configuration = configuration
-          @id = Id.generate
+          @id = ::Xml::Kit::Id.generate
           @issuer = configuration.issuer
           @now = Time.now.utc
           @request = request
lib/saml/kit/builders/metadata.rb
@@ -1,9 +1,10 @@
 module Saml
   module Kit
     module Builders
+      # {include:file:lib/saml/kit/builders/templates/metadata.builder}
       # {include:file:spec/saml/builders/metadata_spec.rb}
       class Metadata
-        include Templatable
+        include XmlTemplatable
 
         attr_accessor :entity_id
         attr_accessor :id
@@ -13,7 +14,7 @@ module Saml
         attr_reader :configuration
 
         def initialize(configuration: Saml::Kit.configuration)
-          @id = Id.generate
+          @id = ::Xml::Kit::Id.generate
           @entity_id = configuration.issuer
           @configuration = configuration
         end
@@ -39,7 +40,7 @@ module Saml
         def entity_descriptor_options
           {
             'xmlns': Namespaces::METADATA,
-            'xmlns:ds': Namespaces::XMLDSIG,
+            'xmlns:ds': ::Xml::Kit::Namespaces::XMLDSIG,
             'xmlns:saml': Namespaces::ASSERTION,
             ID: id,
             entityID: entity_id,
lib/saml/kit/builders/response.rb
@@ -1,20 +1,21 @@
 module Saml
   module Kit
     module Builders
+      # {include:file:lib/saml/kit/builders/templates/response.builder}
       # {include:file:spec/saml/builders/response_spec.rb}
       class Response
-        include Templatable
+        include XmlTemplatable
         attr_reader :user, :request
         attr_accessor :id, :reference_id, :now
         attr_accessor :version, :status_code
-        attr_accessor :issuer, :destination, :encrypt
+        attr_accessor :issuer, :destination
         attr_reader :configuration
 
         def initialize(user, request, configuration: Saml::Kit.configuration)
           @user = user
           @request = request
-          @id = Id.generate
-          @reference_id = Id.generate
+          @id = ::Xml::Kit::Id.generate
+          @reference_id = ::Xml::Kit::Id.generate
           @now = Time.now.utc
           @version = "2.0"
           @status_code = Namespaces::SUCCESS
@@ -37,7 +38,15 @@ module Saml
         private
 
         def assertion
-          @assertion ||= Saml::Kit::Builders::Assertion.new(self)
+          @assertion ||=
+            begin
+              assertion = Saml::Kit::Builders::Assertion.new(self)
+              if encrypt
+                Saml::Kit::Builders::EncryptedAssertion.new(self, assertion)
+              else
+                assertion
+              end
+            end
         end
 
         def response_options
lib/saml/kit/builders/service_provider_metadata.rb
@@ -1,9 +1,10 @@
 module Saml
   module Kit
     module Builders
+      # {include:file:lib/saml/kit/builders/templates/service_provider_metadata.builder}
       # {include:file:spec/saml/builders/service_provider_metadata_spec.rb}
       class ServiceProviderMetadata
-        include Saml::Kit::Templatable
+        include XmlTemplatable
         extend Forwardable
         attr_accessor :acs_urls, :logout_urls, :name_id_formats
         attr_accessor :want_assertions_signed
lib/saml/kit/builders/xml_encryption.rb
@@ -1,20 +0,0 @@
-module Saml
-  module Kit
-    module Builders
-      class XmlEncryption
-        attr_reader :public_key
-        attr_reader :key, :iv, :encrypted
-
-        def initialize(raw_xml, public_key)
-          @public_key = public_key
-          cipher = OpenSSL::Cipher.new('AES-256-CBC')
-          cipher.encrypt
-          @key = cipher.random_key
-          @iv = cipher.random_iv
-          @encrypted = cipher.update(raw_xml) + cipher.final
-        end
-      end
-    end
-  end
-end
-
lib/saml/kit/builders/xml_signature.rb
@@ -1,40 +0,0 @@
-module Saml
-  module Kit
-    module Builders
-      class XmlSignature
-        SIGNATURE_METHODS = {
-          SHA1: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
-          SHA224: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha224",
-          SHA256: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
-          SHA384: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
-          SHA512: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
-        }.freeze
-        DIGEST_METHODS = {
-          SHA1: "http://www.w3.org/2000/09/xmldsig#SHA1",
-          SHA224: "http://www.w3.org/2001/04/xmldsig-more#sha224",
-          SHA256: "http://www.w3.org/2001/04/xmlenc#sha256",
-          SHA384: "http://www.w3.org/2001/04/xmldsig-more#sha384",
-          SHA512: "http://www.w3.org/2001/04/xmlenc#sha512",
-        }.freeze
-
-        attr_reader :embed_signature, :configuration
-        attr_reader :reference_id
-        attr_reader :certificate
-
-        def initialize(reference_id, configuration:, certificate: )
-          @configuration = configuration
-          @reference_id = reference_id
-          @certificate = certificate
-        end
-
-        def signature_method
-          SIGNATURE_METHODS[configuration.signature_method]
-        end
-
-        def digest_method
-          DIGEST_METHODS[configuration.digest_method]
-        end
-      end
-    end
-  end
-end
lib/saml/kit/crypto/oaep_cipher.rb
@@ -1,22 +0,0 @@
-module Saml
-  module Kit
-    module Crypto
-      class OaepCipher
-        ALGORITHMS = {
-          'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' => true,
-        }
-        def initialize(algorithm, key)
-          @key = key
-        end
-
-        def self.matches?(algorithm)
-          ALGORITHMS[algorithm]
-        end
-
-        def decrypt(cipher_text)
-          @key.private_decrypt(cipher_text, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
-        end
-      end
-    end
-  end
-end
lib/saml/kit/crypto/rsa_cipher.rb
@@ -1,23 +0,0 @@
-module Saml
-  module Kit
-    module Crypto
-      class RsaCipher
-        ALGORITHMS = {
-          'http://www.w3.org/2001/04/xmlenc#rsa-1_5' => true,
-        }
-
-        def initialize(algorithm, key)
-          @key = key
-        end
-
-        def self.matches?(algorithm)
-          ALGORITHMS[algorithm]
-        end
-
-        def decrypt(cipher_text)
-          @key.private_decrypt(cipher_text)
-        end
-      end
-    end
-  end
-end
lib/saml/kit/crypto/simple_cipher.rb
@@ -1,38 +0,0 @@
-module Saml
-  module Kit
-    module Crypto
-      class SimpleCipher
-        ALGORITHMS = {
-          'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => 'DES-EDE3-CBC',
-          'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => 'AES-128-CBC',
-          'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => 'AES-192-CBC',
-          'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => 'AES-256-CBC',
-        }
-
-        def initialize(algorithm, private_key)
-          @algorithm = algorithm
-          @private_key = private_key
-        end
-
-        def self.matches?(algorithm)
-          ALGORITHMS[algorithm]
-        end
-
-        def decrypt(cipher_text)
-          cipher = OpenSSL::Cipher.new(ALGORITHMS[@algorithm])
-          cipher.decrypt
-          iv = cipher_text[0..cipher.iv_len-1]
-          data = cipher_text[cipher.iv_len..-1]
-          #cipher.padding = 0
-          cipher.key = @private_key
-          cipher.iv = iv
-
-          Saml::Kit.logger.debug ['-key', @private_key].inspect
-          Saml::Kit.logger.debug ['-iv', iv].inspect
-
-          cipher.update(data) + cipher.final
-        end
-      end
-    end
-  end
-end
lib/saml/kit/crypto/unknown_cipher.rb
@@ -1,18 +0,0 @@
-module Saml
-  module Kit
-    module Crypto
-      class UnknownCipher
-        def initialize(algorithm, key)
-        end
-
-        def self.matches?(algorithm)
-          true
-        end
-
-        def decrypt(cipher_text)
-          cipher_text
-        end
-      end
-    end
-  end
-end
lib/saml/kit/assertion.rb
@@ -72,17 +72,20 @@ module Saml
       attr_reader :configuration
 
       def assertion
-        @assertion ||= if encrypted?
-          decrypted = XmlDecryption.new(configuration: configuration).decrypt(@xml_hash['Response']['EncryptedAssertion'])
-          Saml::Kit.logger.debug(decrypted)
-          Hash.from_xml(decrypted)['Assertion']
-        else
-          result = @xml_hash.fetch('Response', {}).fetch('Assertion', {})
-          return result if result.is_a?(Hash)
-
-          errors[:assertion] << error_message(:must_contain_single_assertion)
-          {}
-        end
+        @assertion ||=
+          if encrypted?
+            private_keys = configuration.private_keys(use: :encryption)
+            decryptor = ::Xml::Kit::Decryption.new(private_keys: private_keys)
+            decrypted = decryptor.decrypt(@xml_hash['Response']['EncryptedAssertion'])
+            Saml::Kit.logger.debug(decrypted)
+            Hash.from_xml(decrypted)['Assertion']
+          else
+            result = @xml_hash.fetch('Response', {}).fetch('Assertion', {})
+            return result if result.is_a?(Hash)
+
+            errors[:assertion] << error_message(:must_contain_single_assertion)
+            {}
+          end
       end
 
       def parse_date(value)
lib/saml/kit/builders.rb
@@ -1,13 +1,13 @@
+require 'saml/kit/xml_templatable'
 require 'saml/kit/builders/assertion'
 require 'saml/kit/builders/authentication_request'
+require 'saml/kit/builders/encrypted_assertion'
 require 'saml/kit/builders/identity_provider_metadata'
 require 'saml/kit/builders/logout_request'
 require 'saml/kit/builders/logout_response'
 require 'saml/kit/builders/metadata'
 require 'saml/kit/builders/response'
 require 'saml/kit/builders/service_provider_metadata'
-require 'saml/kit/builders/xml_encryption'
-require 'saml/kit/builders/xml_signature'
 
 module Saml
   module Kit
lib/saml/kit/certificate.rb
@@ -1,96 +0,0 @@
-module Saml
-  module Kit
-    # {include:file:spec/saml/certificate_spec.rb}
-    class Certificate
-      BEGIN_CERT=/-----BEGIN CERTIFICATE-----/
-      END_CERT=/-----END CERTIFICATE-----/
-      # The use can be `:signing` or `:encryption`
-      attr_reader :use
-
-      def initialize(value, use:)
-        @value = value
-        @use = use.downcase.to_sym
-      end
-
-      # @return [Saml::Kit::Fingerprint] the certificate fingerprint.
-      def fingerprint
-        Fingerprint.new(value)
-      end
-
-      # Returns true if this certificate is for the specified use.
-      #
-      # @param use [Symbol] `:signing` or `:encryption`.
-      # @return [Boolean] true or false.
-      def for?(use)
-        self.use == use.to_sym
-      end
-
-      # Returns true if this certificate is used for encryption.
-      #
-      # return [Boolean] true or false.
-      def encryption?
-        for?(:encryption)
-      end
-
-      # Returns true if this certificate is used for signing.
-      #
-      # return [Boolean] true or false.
-      def signing?
-        for?(:signing)
-      end
-
-      # Returns the x509 form.
-      #
-      # return [OpenSSL::X509::Certificate] the OpenSSL equivalent.
-      def x509
-        self.class.to_x509(value)
-      end
-
-      # Returns the public key.
-      #
-      # @return [OpenSSL::PKey::RSA] the RSA public key.
-      def public_key
-        x509.public_key
-      end
-
-      def ==(other)
-        self.fingerprint == other.fingerprint
-      end
-
-      def eql?(other)
-        self == other
-      end
-
-      def hash
-        value.hash
-      end
-
-      def to_s
-        value
-      end
-
-      def to_h
-        { use: @use, fingerprint: fingerprint.to_s }
-      end
-
-      def inspect
-        to_h.inspect
-      end
-
-      def stripped
-        value.to_s.gsub(BEGIN_CERT, '').gsub(END_CERT, '').gsub(/\n/, '')
-      end
-
-      def self.to_x509(value)
-        OpenSSL::X509::Certificate.new(Base64.decode64(value))
-      rescue OpenSSL::X509::CertificateError => error
-        Saml::Kit.logger.warn(error)
-        OpenSSL::X509::Certificate.new(value)
-      end
-
-      private
-
-      attr_reader :value
-    end
-  end
-end
lib/saml/kit/configuration.rb
@@ -53,7 +53,7 @@ module Saml
       # @param passphrase [String] the password to decrypt the private key.
       # @param use [Symbol] the type of key pair, `:signing` or `:encryption`
       def add_key_pair(certificate, private_key, passphrase: '', use: :signing)
-        @key_pairs.push(KeyPair.new(certificate, private_key, passphrase, use.to_sym))
+        @key_pairs.push(::Xml::Kit::KeyPair.new(certificate, private_key, passphrase, use.to_sym))
       end
 
       # Generates a unique key pair that can be used for signing or encryption.
@@ -61,7 +61,7 @@ module Saml
       # @param use [Symbol] the type of key pair, `:signing` or `:encryption`
       # @param passphrase [String] the private key passphrase to use.
       def generate_key_pair_for(use:, passphrase: SecureRandom.uuid)
-        certificate, private_key = SelfSignedCertificate.new(passphrase).create
+        certificate, private_key = ::Xml::Kit::SelfSignedCertificate.new(passphrase).create
         add_key_pair(certificate, private_key, passphrase: passphrase, use: use)
       end
 
lib/saml/kit/crypto.rb
@@ -1,17 +0,0 @@
-require 'saml/kit/crypto/oaep_cipher'
-require 'saml/kit/crypto/rsa_cipher'
-require 'saml/kit/crypto/simple_cipher'
-require 'saml/kit/crypto/unknown_cipher'
-
-module Saml
-  module Kit
-    module Crypto
-      DECRYPTORS = [ SimpleCipher, RsaCipher, OaepCipher, UnknownCipher ]
-
-      # @!visibility private
-      def self.decryptor_for(algorithm, key)
-        DECRYPTORS.find { |x| x.matches?(algorithm) }.new(algorithm, key)
-      end
-    end
-  end
-end
lib/saml/kit/document.rb
@@ -2,6 +2,13 @@ module Saml
   module Kit
     class Document
       PROTOCOL_XSD = File.expand_path("./xsd/saml-schema-protocol-2.0.xsd", File.dirname(__FILE__)).freeze
+      NAMESPACES = {
+        "NameFormat": ::Saml::Kit::Namespaces::ATTR_SPLAT,
+        "ds": ::Xml::Kit::Namespaces::XMLDSIG,
+        "md": ::Saml::Kit::Namespaces::METADATA,
+        "saml": ::Saml::Kit::Namespaces::ASSERTION,
+        "samlp": ::Saml::Kit::Namespaces::PROTOCOL,
+      }.freeze
       include ActiveModel::Validations
       include XsdValidatable
       include Translatable
@@ -79,12 +86,15 @@ module Saml
         # @param xml [String] the raw xml string.
         # @param configuration [Saml::Kit::Configuration] the configuration to use for unpacking the document.
         def to_saml_document(xml, configuration: Saml::Kit.configuration)
+          xml_document = ::Xml::Kit::Document.new(xml, namespaces: {
+            "samlp": ::Saml::Kit::Namespaces::PROTOCOL
+          })
           constructor = {
             "AuthnRequest" => Saml::Kit::AuthenticationRequest,
             "LogoutRequest" => Saml::Kit::LogoutRequest,
             "LogoutResponse" => Saml::Kit::LogoutResponse,
             "Response" => Saml::Kit::Response,
-          }[Saml::Kit::Xml.new(xml).find_by(XPATH).name] || InvalidDocument
+          }[xml_document.find_by(XPATH).name] || InvalidDocument
           constructor.new(xml, configuration: configuration)
         rescue => error
           Saml::Kit.logger.error(error)
lib/saml/kit/fingerprint.rb
@@ -1,50 +0,0 @@
-module Saml
-  module Kit
-    # This generates a fingerprint for an X509 Certificate.
-    #
-    #   certificate, _ = Saml::Kit::SelfSignedCertificate.new("password").create
-    #
-    #   puts Saml::Kit::Fingerprint.new(certificate).to_s
-    #   # B7:AB:DC:BD:4D:23:58:65:FD:1A:99:0C:5F:89:EA:87:AD:F1:D7:83:34:7A:E9:E4:88:12:DD:46:1F:38:05:93
-    #
-    # {include:file:spec/saml/fingerprint_spec.rb}
-    class Fingerprint
-      # The OpenSSL::X509::Certificate
-      attr_reader :x509
-
-      def initialize(raw_certificate)
-        @x509 = Certificate.to_x509(raw_certificate)
-      end
-
-      # Generates a formatted fingerprint using the specified hash algorithm.
-      #
-      # @param algorithm [OpenSSL::Digest] the openssl algorithm to use `OpenSSL::Digest::SHA256`, `OpenSSL::Digest::SHA1`.
-      # @return [String] in the format of `"BF:ED:C5:F1:6C:AB:F5:B2:15:1F:BF:BD:7D:68:1A:F9:A5:4E:4C:19:30:BC:6D:25:B1:8E:98:D4:23:FD:B4:09"`
-      def algorithm(algorithm)
-        pretty_fingerprint(algorithm.new.hexdigest(x509.to_der))
-      end
-
-      def ==(other)
-        self.to_s == other.to_s
-      end
-
-      def eql?(other)
-        self == other
-      end
-
-      def hash
-        to_s.hash
-      end
-
-      def to_s
-        algorithm(OpenSSL::Digest::SHA256)
-      end
-
-      private
-
-      def pretty_fingerprint(fingerprint)
-        fingerprint.upcase.scan(/../).join(":")
-      end
-    end
-  end
-end
lib/saml/kit/id.rb
@@ -1,14 +0,0 @@
-module Saml
-  module Kit
-    # This class is used primary for generating ID.
-    #https://www.w3.org/2001/XMLSchema.xsd
-    class Id
-
-     # Generate an ID that conforms to the XML Schema.
-      # https://www.w3.org/2001/XMLSchema.xsd
-      def self.generate
-        "_#{SecureRandom.uuid}"
-      end
-    end
-  end
-end
lib/saml/kit/key_pair.rb
@@ -1,29 +0,0 @@
-module Saml
-  module Kit
-    class KeyPair # :nodoc:
-      attr_reader :certificate, :private_key, :use
-
-      def initialize(certificate, private_key, passphrase, use)
-        @use = use
-        @certificate = Saml::Kit::Certificate.new(certificate, use: use)
-        @private_key = OpenSSL::PKey::RSA.new(private_key, passphrase)
-      end
-
-      # Returns true if the key pair is the designated use.
-      #
-      # @param use [Symbol] Can be either `:signing` or `:encryption`.
-      def for?(use)
-        @use == use
-      end
-
-      # Returns a generated self signed certificate with private key.
-      #
-      # @param use [Symbol] Can be either `:signing` or `:encryption`.
-      # @param passphrase [String] the passphrase to use to encrypt the private key.
-      def self.generate(use:, passphrase: SecureRandom.uuid)
-        certificate, private_key = SelfSignedCertificate.new(passphrase).create
-        new(certificate, private_key, passphrase, use)
-      end
-    end
-  end
-end
lib/saml/kit/metadata.rb
@@ -24,6 +24,13 @@ module Saml
     # {include:file:spec/examples/metadata_spec.rb}
     class Metadata
       METADATA_XSD = File.expand_path("./xsd/saml-schema-metadata-2.0.xsd", File.dirname(__FILE__)).freeze
+      NAMESPACES = {
+        "NameFormat": Namespaces::ATTR_SPLAT,
+        "ds": ::Xml::Kit::Namespaces::XMLDSIG,
+        "md": Namespaces::METADATA,
+        "saml": Namespaces::ASSERTION,
+        "samlp": Namespaces::PROTOCOL,
+      }.freeze
       include ActiveModel::Validations
       include XsdValidatable
       include Translatable
@@ -69,8 +76,8 @@ module Saml
       # Returns each of the X509 certificates.
       def certificates
         @certificates ||= document.find_all("/md:EntityDescriptor/md:#{name}/md:KeyDescriptor").map do |item|
-          cert = item.at_xpath("./ds:KeyInfo/ds:X509Data/ds:X509Certificate", Xml::NAMESPACES).text
-          Certificate.new(cert, use: item.attribute('use').value.to_sym)
+          cert = item.at_xpath("./ds:KeyInfo/ds:X509Data/ds:X509Certificate", NAMESPACES).text
+          ::Xml::Kit::Certificate.new(cert, use: item.attribute('use').value.to_sym)
         end
       end
 
@@ -134,7 +141,7 @@ module Saml
       #
       # @param fingerprint [Saml::Kit::Fingerprint] the fingerprint to search for.
       # @param use [Symbol] the type of certificates to look at. Can be `:signing` or `:encryption`.
-      # @return [Saml::Kit::Certificate] returns the matching `{Saml::Kit::Certificate}`
+      # @return [Xml::Kit::Certificate] returns the matching `{Xml::Kit::Certificate}`
       def matches?(fingerprint, use: :signing)
         certificates.find do |certificate|
           certificate.for?(use) && certificate.fingerprint == fingerprint
@@ -163,7 +170,7 @@ module Saml
       # @param algorithm [OpenSSL::Digest] the digest algorithm to use. E.g. `OpenSSL::Digest::SHA256`
       # @param signature [String] the signature to verify
       # @param data [String] the data that is used to produce the signature.
-      # @return [Saml::Kit::Certificate] the certificate that was used to produce the signature.
+      # @return [Xml::Kit::Certificate] the certificate that was used to produce the signature.
       def verify(algorithm, signature, data)
         signing_certificates.find do |certificate|
           certificate.public_key.verify(algorithm, signature, data)
@@ -196,7 +203,7 @@ module Saml
       attr_reader :xml
 
       def document
-        @document ||= Xml.new(xml)
+        @document ||= ::Xml::Kit::Document.new(xml, namespaces: NAMESPACES)
       end
 
       def metadata
@@ -220,7 +227,7 @@ module Saml
       end
 
       def valid_signature?
-        xml = Saml::Kit::Xml.new(to_xml)
+        xml = document
         result = xml.valid?
         xml.errors.each do |error|
           errors[:base] << error
lib/saml/kit/namespaces.rb
@@ -6,7 +6,6 @@ module Saml
       BASIC = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
       BEARER = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
       EMAIL_ADDRESS = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
-      ENVELOPED_SIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
       METADATA = "urn:oasis:names:tc:SAML:2.0:metadata"
       PASSWORD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
       PASSWORD_PROTECTED = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
@@ -14,22 +13,12 @@ module Saml
       PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
       REQUESTER_ERROR = "urn:oasis:names:tc:SAML:2.0:status:Requester"
       RESPONDER_ERROR = "urn:oasis:names:tc:SAML:2.0:status:Responder"
-      RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
-      RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
-      RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
-      RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
-      SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
-      SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256'
-      SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"
-      SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512'
       SUCCESS = "urn:oasis:names:tc:SAML:2.0:status:Success"
       TRANSIENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
       UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:consent:unspecified"
       UNSPECIFIED_NAMEID = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
       URI = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
       VERSION_MISMATCH_ERROR = "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch"
-      XMLDSIG = "http://www.w3.org/2000/09/xmldsig#"
-      XMLENC = "http://www.w3.org/2001/04/xmlenc#"
     end
   end
 end
lib/saml/kit/self_signed_certificate.rb
@@ -1,28 +0,0 @@
-module Saml
-  module Kit
-    class SelfSignedCertificate
-      SUBJECT="/C=CA/ST=Alberta/L=Calgary/O=SamlKit/OU=SamlKit/CN=SamlKit"
-
-      def initialize(passphrase)
-        @passphrase = passphrase
-      end
-
-      def create
-        rsa_key = OpenSSL::PKey::RSA.new(2048)
-        public_key = rsa_key.public_key
-        certificate = OpenSSL::X509::Certificate.new
-        certificate.subject = certificate.issuer = OpenSSL::X509::Name.parse(SUBJECT)
-        certificate.not_before = DateTime.now.beginning_of_day
-        certificate.not_after = 30.days.from_now
-        certificate.public_key = public_key
-        certificate.serial = 0x0
-        certificate.version = 2
-        certificate.sign(rsa_key, OpenSSL::Digest::SHA256.new)
-        [
-          certificate.to_pem,
-          rsa_key.to_pem(OpenSSL::Cipher.new('AES-256-CBC'), @passphrase)
-        ]
-      end
-    end
-  end
-end
lib/saml/kit/signature.rb
@@ -9,7 +9,7 @@ module Saml
       def certificate
         value = to_h.fetch('KeyInfo', {}).fetch('X509Data', {}).fetch('X509Certificate', nil)
         return if value.nil?
-        Saml::Kit::Certificate.new(value, use: :signing)
+        ::Xml::Kit::Certificate.new(value, use: :signing)
       end
 
       # Returns true when the fingerprint of the certificate matches one of the certificates registered in the metadata.
lib/saml/kit/signatures.rb
@@ -1,57 +0,0 @@
-module Saml
-  module Kit
-    # @!visibility private
-    class Signatures # :nodoc:
-      # @!visibility private
-      attr_reader :configuration
-
-      # @!visibility private
-      def initialize(configuration:)
-        @configuration = configuration
-        @key_pair = configuration.key_pairs(use: :signing).last
-      end
-
-      # @!visibility private
-      def sign_with(key_pair)
-        @key_pair = key_pair
-      end
-
-      # @!visibility private
-      def build(reference_id)
-        return nil unless configuration.sign?
-        certificate = @key_pair.certificate
-        Saml::Kit::Builders::XmlSignature.new(reference_id, configuration: configuration, certificate: certificate)
-      end
-
-      # @!visibility private
-      def complete(raw_xml)
-        return raw_xml unless configuration.sign?
-        private_key = @key_pair.private_key
-        Xmldsig::SignedDocument.new(raw_xml).sign(private_key)
-      end
-
-      # @!visibility private
-      def self.sign(xml: ::Builder::XmlMarkup.new, configuration: Saml::Kit.configuration)
-        signatures = Saml::Kit::Signatures.new(configuration: configuration)
-        yield xml, XmlSignatureTemplate.new(xml, signatures)
-        signatures.complete(xml.target!)
-      end
-
-      class XmlSignatureTemplate # :nodoc:
-        # @!visibility private
-        attr_reader :signatures, :xml
-
-        # @!visibility private
-        def initialize(xml, signatures)
-          @signatures = signatures
-          @xml = xml
-        end
-
-        # @!visibility private
-        def template(reference_id)
-          Template.new(signatures.build(reference_id)).to_xml(xml: xml)
-        end
-      end
-    end
-  end
-end
lib/saml/kit/templatable.rb
@@ -1,67 +0,0 @@
-module Saml
-  module Kit
-    module Templatable
-      # Can be used to disable embeding a signature.
-      # By default a signature will be embedded if a signing
-      # certificate is available via the configuration.
-      attr_accessor :embed_signature
-
-      # @deprecated Use {#embed_signature=} instead of this method.
-      def sign=(value)
-        Saml::Kit.deprecate("sign= is deprecated. Use embed_signature= instead")
-        self.embed_signature = value
-      end
-
-      # Returns the generated XML document with an XML Digital Signature and XML Encryption.
-      def to_xml(xml: ::Builder::XmlMarkup.new)
-        signatures.complete(render(self, xml: xml))
-      end
-
-      # @!visibility private
-      def signature_for(reference_id:, xml:)
-        return unless sign?
-        render(signatures.build(reference_id), xml: xml)
-      end
-
-      # Allows you to specify which key pair to use for generating an XML digital signature.
-      #
-      # @param key_pair [Saml::Kit::KeyPair] the key pair to use for signing.
-      def sign_with(key_pair)
-        signatures.sign_with(key_pair)
-      end
-
-      # Returns true if an embedded signature is requested and at least one signing certificate is available via the configuration.
-      def sign?
-        embed_signature.nil? ? configuration.sign? : embed_signature && configuration.sign?
-      end
-
-      # @!visibility private
-      def signatures
-        @signatures ||= Saml::Kit::Signatures.new(configuration: configuration)
-      end
-
-      # @!visibility private
-      def encryption_for(xml:)
-        if encrypt?
-          temp = ::Builder::XmlMarkup.new
-          yield temp
-          signed_xml = signatures.complete(temp.target!)
-          xml_encryption = Saml::Kit::Builders::XmlEncryption.new(signed_xml, encryption_certificate.public_key)
-          render(xml_encryption, xml: xml)
-        else
-          yield xml
-        end
-      end
-
-      # @!visibility private
-      def encrypt?
-        encrypt && encryption_certificate
-      end
-
-      # @!visibility private
-      def render(model, options)
-        Saml::Kit::Template.new(model).to_xml(options)
-      end
-    end
-  end
-end
lib/saml/kit/template.rb
@@ -1,33 +0,0 @@
-module Saml
-  module Kit
-    class Template
-      attr_reader :target
-
-      def initialize(target)
-        @target = target
-      end
-
-      # Returns the compiled template as a [String].
-      #
-      # @param options [Hash] The options hash to pass to the template engine.
-      def to_xml(options)
-        template.render(target, options)
-      end
-
-      private
-
-      def template_name
-        "#{target.class.name.split("::").last.underscore}.builder"
-      end
-
-      def template_path
-        root_path = File.expand_path(File.dirname(__FILE__))
-        File.join(root_path, "builders/templates/", template_name)
-      end
-
-      def template
-        Tilt.new(template_path)
-      end
-    end
-  end
-end
lib/saml/kit/trustable.rb
@@ -44,7 +44,13 @@ module Saml
       def must_have_valid_signature
         return if to_xml.blank?
 
-        xml = Saml::Kit::Xml.new(to_xml)
+        xml = ::Xml::Kit::Document.new(to_xml, namespaces: {
+          "NameFormat": Namespaces::ATTR_SPLAT,
+          "ds": ::Xml::Kit::Namespaces::XMLDSIG,
+          "md": Namespaces::METADATA,
+          "saml": Namespaces::ASSERTION,
+          "samlp": Namespaces::PROTOCOL,
+        })
         xml.valid?
         xml.errors.each do |error|
           errors[:base] << error
lib/saml/kit/version.rb
@@ -1,5 +1,5 @@
 module Saml
   module Kit
-    VERSION = "0.3.0"
+    VERSION = "0.3.1"
   end
 end
lib/saml/kit/xml.rb
@@ -1,80 +0,0 @@
-module Saml
-  module Kit
-    # {include:file:spec/saml/xml_spec.rb}
-    class Xml # :nodoc:
-      include ActiveModel::Validations
-      NAMESPACES = {
-        "NameFormat": Namespaces::ATTR_SPLAT,
-        "ds": Namespaces::XMLDSIG,
-        "md": Namespaces::METADATA,
-        "saml": Namespaces::ASSERTION,
-        "samlp": Namespaces::PROTOCOL,
-      }.freeze
-
-      validate :validate_signatures
-      validate :validate_certificates
-
-      def initialize(raw_xml)
-        @raw_xml = raw_xml
-        @document = Nokogiri::XML(raw_xml)
-      end
-
-      # Returns the first XML node found by searching the document with the provided XPath.
-      #
-      # @param xpath [String] the XPath to use to search the document
-      def find_by(xpath)
-        document.at_xpath(xpath, NAMESPACES)
-      end
-
-      # Returns all XML nodes found by searching the document with the provided XPath.
-      #
-      # @param xpath [String] the XPath to use to search the document
-      def find_all(xpath)
-        document.search(xpath, NAMESPACES)
-      end
-
-      # Return the XML document as a [String].
-      #
-      # @param pretty [Boolean] return the XML string in a human readable format if true.
-      def to_xml(pretty: true)
-        pretty ? document.to_xml(indent: 2) : raw_xml
-      end
-
-      private
-
-      attr_reader :raw_xml, :document
-
-      def validate_signatures
-        invalid_signatures.flat_map(&:errors).uniq.each do |error|
-          errors.add(error, "is invalid")
-        end
-      end
-
-      def invalid_signatures
-        signed_document = Xmldsig::SignedDocument.new(document, id_attr: 'ID=$uri or @Id')
-        signed_document.signatures.find_all do |signature|
-          x509_certificates.all? do |certificate|
-            !signature.valid?(certificate)
-          end
-        end
-      end
-
-      def validate_certificates(now = Time.current)
-        return if find_by('//ds:Signature').nil?
-
-        x509_certificates.each do |certificate|
-          inactive = now < certificate.not_before
-          errors.add(:certificate, "Not valid before #{certificate.not_before}") if inactive
-
-          expired = now > certificate.not_after
-          errors.add(:certificate, "Not valid after #{certificate.not_after}") if expired
-        end
-      end
-
-      def x509_certificates
-        xpath = "//ds:KeyInfo/ds:X509Data/ds:X509Certificate"
-        find_all(xpath).map { |item| Certificate.to_x509(item.text) }
-      end
-    end
-  end
-end
lib/saml/kit/xml_decryption.rb
@@ -1,44 +0,0 @@
-module Saml
-  module Kit
-    # {include:file:spec/saml/xml_decryption_spec.rb}
-    class XmlDecryption
-      # The list of private keys to use to attempt to decrypt the document.
-      attr_reader :private_keys
-
-      def initialize(configuration: Saml::Kit.configuration)
-        @private_keys = configuration.private_keys(use: :encryption)
-      end
-
-      # Decrypts an EncryptedData section of an XML document.
-      #
-      # @param data [Hash] the XML document converted to a [Hash] using Hash.from_xml.
-      def decrypt(data)
-        encrypted_data = data['EncryptedData']
-        symmetric_key = symmetric_key_from(encrypted_data)
-        cipher_text = Base64.decode64(encrypted_data["CipherData"]["CipherValue"])
-        to_plaintext(cipher_text, symmetric_key, encrypted_data["EncryptionMethod"]['Algorithm'])
-      end
-
-      private
-
-      def symmetric_key_from(encrypted_data)
-        encrypted_key = encrypted_data['KeyInfo']['EncryptedKey']
-        cipher_text = Base64.decode64(encrypted_key['CipherData']['CipherValue'])
-        attempts = private_keys.count
-        private_keys.each do |private_key|
-          begin
-            attempts -= 1
-            return to_plaintext(cipher_text, private_key, encrypted_key["EncryptionMethod"]['Algorithm'])
-          rescue OpenSSL::PKey::RSAError => error
-            Saml::Kit.logger.error(error)
-            raise if attempts.zero?
-          end
-        end
-      end
-
-      def to_plaintext(cipher_text, symmetric_key, algorithm)
-        Crypto.decryptor_for(algorithm, symmetric_key).decrypt(cipher_text)
-      end
-    end
-  end
-end
lib/saml/kit/xml_templatable.rb
@@ -0,0 +1,37 @@
+module Saml
+  module Kit
+    module XmlTemplatable
+      include ::Xml::Kit::Templatable
+
+      def template_path
+        root_path = File.expand_path(File.dirname(__FILE__))
+        template_name = "#{self.class.name.split("::").last.underscore}.builder"
+        File.join(root_path, "builders/templates/", template_name)
+      end
+
+      # Returns true if an embedded signature is requested and at least one signing certificate is available via the configuration.
+      def sign?
+        return configuration.sign? if embed_signature.nil?
+        embed_signature && configuration.sign?
+      end
+
+      # @deprecated Use {#embed_signature=} instead of this method.
+      def sign=(value)
+        Saml::Kit.deprecate("sign= is deprecated. Use embed_signature= instead.")
+        self.embed_signature = value
+      end
+
+      def digest_method
+        configuration.digest_method
+      end
+
+      def signature_method
+        configuration.signature_method
+      end
+
+      def signing_key_pair
+        configuration.key_pairs(use: :signing).last
+      end
+    end
+  end
+end
lib/saml/kit.rb
@@ -7,16 +7,13 @@ require "active_support/core_ext/hash/indifferent_access"
 require "active_support/core_ext/numeric/time"
 require "active_support/deprecation"
 require "active_support/duration"
-require "builder"
 require "logger"
 require "net/http"
 require "nokogiri"
 require "securerandom"
-require "tilt"
-require "xmldsig"
+require "xml/kit"
 
 require "saml/kit/buildable"
-require "saml/kit/templatable"
 require "saml/kit/builders"
 require "saml/kit/namespaces"
 require "saml/kit/serializable"
@@ -30,27 +27,17 @@ require "saml/kit/document"
 require "saml/kit/assertion"
 require "saml/kit/authentication_request"
 require "saml/kit/bindings"
-require "saml/kit/certificate"
 require "saml/kit/configuration"
-require "saml/kit/crypto"
 require "saml/kit/default_registry"
-require "saml/kit/fingerprint"
-require "saml/kit/key_pair"
 require "saml/kit/logout_response"
 require "saml/kit/logout_request"
 require "saml/kit/metadata"
 require "saml/kit/composite_metadata"
 require "saml/kit/response"
-require "saml/kit/id"
 require "saml/kit/identity_provider_metadata"
 require "saml/kit/invalid_document"
-require "saml/kit/self_signed_certificate"
 require "saml/kit/service_provider_metadata"
 require "saml/kit/signature"
-require "saml/kit/signatures"
-require "saml/kit/template"
-require "saml/kit/xml"
-require "saml/kit/xml_decryption"
 
 I18n.load_path += Dir[File.expand_path("kit/locales/*.yml", File.dirname(__FILE__))]
 
spec/saml/bindings/url_builder_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe Saml::Kit::Bindings::UrlBuilder do
         it 'includes a signature' do
           result = subject.build(response, relay_state: relay_state)
           query_params = to_query_params(result)
-          expect(query_params['SigAlg']).to eql(CGI.escape(Saml::Kit::Namespaces::SHA256))
+          expect(query_params['SigAlg']).to eql(CGI.escape(::Xml::Kit::Namespaces::SHA256))
 
           payload = "#{query_string_parameter}=#{query_params[query_string_parameter]}"
           payload << "&RelayState=#{query_params['RelayState']}"
@@ -67,7 +67,7 @@ RSpec.describe Saml::Kit::Bindings::UrlBuilder do
         it 'generates the signature correctly when the relay state is absent' do
           result = subject.build(response)
           query_params = to_query_params(result)
-          expect(query_params['SigAlg']).to eql(CGI.escape(Saml::Kit::Namespaces::SHA256))
+          expect(query_params['SigAlg']).to eql(CGI.escape(::Xml::Kit::Namespaces::SHA256))
 
           payload = "#{query_string_parameter}=#{query_params[query_string_parameter]}"
           payload << "&SigAlg=#{query_params['SigAlg']}"
spec/saml/builders/logout_request_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Saml::Kit::Builders::LogoutRequest do
 
   it 'produces the expected xml' do
     travel_to 1.second.from_now
-    subject.id = Saml::Kit::Id.generate
+    subject.id = Xml::Kit::Id.generate
     subject.destination = FFaker::Internet.http_url
     subject.issuer = FFaker::Internet.http_url
     subject.name_id_format = Saml::Kit::Namespaces::TRANSIENT
spec/saml/builders/response_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Saml::Kit::Builders::Response do
   let(:email) { FFaker::Internet.email }
   let(:assertion_consumer_service_url) { FFaker::Internet.uri("https") }
   let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: { email: email, created_at: Time.now.utc.iso8601 }) }
-  let(:request) { double(:request, id: Saml::Kit::Id.generate, assertion_consumer_service_url: assertion_consumer_service_url, issuer: issuer, name_id_format: Saml::Kit::Namespaces::EMAIL_ADDRESS, provider: provider, trusted?: true, signed?: true) }
+  let(:request) { double(:request, id: Xml::Kit::Id.generate, assertion_consumer_service_url: assertion_consumer_service_url, issuer: issuer, name_id_format: Saml::Kit::Namespaces::EMAIL_ADDRESS, provider: provider, trusted?: true, signed?: true) }
   let(:provider) { double(:provider, want_assertions_signed: false, encryption_certificates: [configuration.certificates(use: :encryption).last] ) }
   let(:issuer) { FFaker::Internet.uri("https") }
 
@@ -93,7 +93,7 @@ RSpec.describe Saml::Kit::Builders::Response do
       result = Hash.from_xml(subject.to_xml)
       expect(result['Response']['EncryptedAssertion']).to be_present
       encrypted_assertion = result['Response']['EncryptedAssertion']
-      decrypted_assertion = Saml::Kit::XmlDecryption.new(configuration: configuration).decrypt(encrypted_assertion)
+      decrypted_assertion = Xml::Kit::Decryption.new(private_keys: configuration.private_keys(use: :encryption)).decrypt(encrypted_assertion)
       decrypted_hash = Hash.from_xml(decrypted_assertion)
       expect(decrypted_hash['Assertion']).to be_present
       expect(decrypted_hash['Assertion']['Issuer']).to be_present
spec/saml/authentication_request_spec.rb
@@ -1,6 +1,6 @@
 RSpec.describe Saml::Kit::AuthenticationRequest do
   subject { described_class.new(raw_xml, configuration: configuration) }
-  let(:id) { Saml::Kit::Id.generate }
+  let(:id) { Xml::Kit::Id.generate }
   let(:assertion_consumer_service_url) { "https://#{FFaker::Internet.domain_name}/acs" }
   let(:issuer) { FFaker::Movie.title }
   let(:destination) { FFaker::Internet.http_url }
@@ -79,10 +79,9 @@ RSpec.describe Saml::Kit::AuthenticationRequest do
     end
 
     it 'validates the schema of the request' do
-      id = Saml::Kit::Id.generate
-      configuration = Saml::Kit::Configuration.new
-      configuration.generate_key_pair_for(use: :signing)
-      signed_xml = Saml::Kit::Signatures.sign(configuration: configuration) do |xml, signature|
+      id = Xml::Kit::Id.generate
+      key_pair = ::Xml::Kit::KeyPair.generate(use: :signing)
+      signed_xml = ::Xml::Kit::Signatures.sign(key_pair: key_pair) do |xml, signature|
         xml.tag!('samlp:AuthnRequest', "xmlns:samlp" => Saml::Kit::Namespaces::PROTOCOL, AssertionConsumerServiceURL: assertion_consumer_service_url, ID: id) do
           signature.template(id)
           xml.Fake do
@@ -96,7 +95,7 @@ RSpec.describe Saml::Kit::AuthenticationRequest do
     it 'validates a request without a signature' do
       now = Time.now.utc
       raw_xml = <<-XML
-<samlp:AuthnRequest AssertionConsumerServiceURL='#{assertion_consumer_service_url}' ID='#{Saml::Kit::Id.generate}' IssueInstant='#{now.iso8601}' Version='2.0' xmlns:saml='#{Saml::Kit::Namespaces::ASSERTION}' xmlns:samlp='#{Saml::Kit::Namespaces::PROTOCOL}'>
+<samlp:AuthnRequest AssertionConsumerServiceURL='#{assertion_consumer_service_url}' ID='#{Xml::Kit::Id.generate}' IssueInstant='#{now.iso8601}' Version='2.0' xmlns:saml='#{Saml::Kit::Namespaces::ASSERTION}' xmlns:samlp='#{Saml::Kit::Namespaces::PROTOCOL}'>
   <saml:Issuer>#{issuer}</saml:Issuer>
   <samlp:NameIDPolicy AllowCreate='true' Format='#{Saml::Kit::Namespaces::EMAIL_ADDRESS}'/>
 </samlp:AuthnRequest>
@@ -110,7 +109,7 @@ RSpec.describe Saml::Kit::AuthenticationRequest do
     it 'is valid when there is no signature, and the issuer is registered' do
       now = Time.now.utc
       raw_xml = <<-XML
-<samlp:AuthnRequest AssertionConsumerServiceURL='#{assertion_consumer_service_url}' ID='#{Saml::Kit::Id.generate}' IssueInstant='#{now.iso8601}' Version='2.0' xmlns:saml='#{Saml::Kit::Namespaces::ASSERTION}' xmlns:samlp='#{Saml::Kit::Namespaces::PROTOCOL}'>
+<samlp:AuthnRequest AssertionConsumerServiceURL='#{assertion_consumer_service_url}' ID='#{Xml::Kit::Id.generate}' IssueInstant='#{now.iso8601}' Version='2.0' xmlns:saml='#{Saml::Kit::Namespaces::ASSERTION}' xmlns:samlp='#{Saml::Kit::Namespaces::PROTOCOL}'>
   <saml:Issuer>#{issuer}</saml:Issuer>
   <samlp:NameIDPolicy AllowCreate='true' Format='#{Saml::Kit::Namespaces::PERSISTENT}'/>
 </samlp:AuthnRequest>
@@ -124,7 +123,7 @@ RSpec.describe Saml::Kit::AuthenticationRequest do
     it 'is invalid when there is no signature, and the issuer is not registered' do
       now = Time.now.utc
       raw_xml = <<-XML
-<samlp:AuthnRequest AssertionConsumerServiceURL='#{assertion_consumer_service_url}' ID='#{Saml::Kit::Id.generate}' IssueInstant='#{now.iso8601}' Version='2.0' xmlns:saml='#{Saml::Kit::Namespaces::ASSERTION}' xmlns:samlp='#{Saml::Kit::Namespaces::PROTOCOL}'>
+<samlp:AuthnRequest AssertionConsumerServiceURL='#{assertion_consumer_service_url}' ID='#{Xml::Kit::Id.generate}' IssueInstant='#{now.iso8601}' Version='2.0' xmlns:saml='#{Saml::Kit::Namespaces::ASSERTION}' xmlns:samlp='#{Saml::Kit::Namespaces::PROTOCOL}'>
   <saml:Issuer>#{issuer}</saml:Issuer>
   <samlp:NameIDPolicy AllowCreate='true' Format='#{Saml::Kit::Namespaces::PERSISTENT}'/>
 </samlp:AuthnRequest>
spec/saml/certificate_spec.rb
@@ -1,13 +0,0 @@
-RSpec.describe Saml::Kit::Certificate do
-  subject { described_class.new(certificate, use: :signing) }
-  let(:certificate) do
-    cert, _ = Saml::Kit::SelfSignedCertificate.new('password').create
-    cert
-  end
-
-  describe "#fingerprint" do
-    it 'returns a fingerprint' do
-      expect(subject.fingerprint).to be_instance_of(Saml::Kit::Fingerprint)
-    end
-  end
-end
spec/saml/composite_metadata_spec.rb
@@ -7,23 +7,23 @@ RSpec.describe Saml::Kit::CompositeMetadata do
   let(:sp_logout_service) { FFaker::Internet.uri("https") }
   let(:idp_logout_service) { FFaker::Internet.uri("https") }
   let(:entity_id) { FFaker::Internet.uri("https") }
-  let(:sp_signing_certificate) { Saml::Kit::KeyPair.generate(use: :signing).certificate }
-  let(:sp_encryption_certificate) { Saml::Kit::KeyPair.generate(use: :encryption).certificate }
-  let(:idp_signing_certificate) { Saml::Kit::KeyPair.generate(use: :signing).certificate }
-  let(:idp_encryption_certificate) { Saml::Kit::KeyPair.generate(use: :encryption).certificate }
+  let(:sp_signing_certificate) { ::Xml::Kit::KeyPair.generate(use: :signing).certificate }
+  let(:sp_encryption_certificate) { ::Xml::Kit::KeyPair.generate(use: :encryption).certificate }
+  let(:idp_signing_certificate) { ::Xml::Kit::KeyPair.generate(use: :signing).certificate }
+  let(:idp_encryption_certificate) { ::Xml::Kit::KeyPair.generate(use: :encryption).certificate }
   let(:xml) do
     <<-XML
-<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{Saml::Kit::Id.generate}" entityID="#{entity_id}">
+<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{::Xml::Kit::Id.generate}" entityID="#{entity_id}">
   <SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="#{Saml::Kit::Namespaces::PROTOCOL}">
     <KeyDescriptor use="signing">
-      <KeyInfo xmlns="#{Saml::Kit::Namespaces::XMLDSIG}">
+      <KeyInfo xmlns="#{::Xml::Kit::Namespaces::XMLDSIG}">
         <X509Data>
           <X509Certificate>#{sp_signing_certificate.stripped}</X509Certificate>
         </X509Data>
       </KeyInfo>
     </KeyDescriptor>
     <KeyDescriptor use="encryption">
-      <KeyInfo xmlns="#{Saml::Kit::Namespaces::XMLDSIG}">
+      <KeyInfo xmlns="#{::Xml::Kit::Namespaces::XMLDSIG}">
         <X509Data>
           <X509Certificate>#{sp_encryption_certificate.stripped}</X509Certificate>
         </X509Data>
@@ -35,14 +35,14 @@ RSpec.describe Saml::Kit::CompositeMetadata do
   </SPSSODescriptor>
   <IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="#{Saml::Kit::Namespaces::PROTOCOL}">
     <KeyDescriptor use="signing">
-      <KeyInfo xmlns="#{Saml::Kit::Namespaces::XMLDSIG}">
+      <KeyInfo xmlns="#{::Xml::Kit::Namespaces::XMLDSIG}">
         <X509Data>
           <X509Certificate>#{idp_signing_certificate.stripped}</X509Certificate>
         </X509Data>
       </KeyInfo>
     </KeyDescriptor>
     <KeyDescriptor use="encryption">
-      <KeyInfo xmlns="#{Saml::Kit::Namespaces::XMLDSIG}">
+      <KeyInfo xmlns="#{::Xml::Kit::Namespaces::XMLDSIG}">
         <X509Data>
           <X509Certificate>#{idp_encryption_certificate.stripped}</X509Certificate>
         </X509Data>
spec/saml/default_registry_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe Saml::Kit::DefaultRegistry do
 
     it 'registers metadata that serves as both an IDP and SP' do
       xml = <<-XML
-<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{Saml::Kit::Id.generate}" entityID="#{entity_id}">
+<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{::Xml::Kit::Id.generate}" entityID="#{entity_id}">
   <SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="#{Saml::Kit::Namespaces::PROTOCOL}">
     <SingleLogoutService Binding="#{Saml::Kit::Bindings::HTTP_POST}" Location="#{FFaker::Internet.uri("https")}"/>
     <NameIDFormat>#{Saml::Kit::Namespaces::PERSISTENT}</NameIDFormat>
spec/saml/document_spec.rb
@@ -2,7 +2,7 @@ RSpec.describe Saml::Kit::Document do
   describe ".to_saml_document" do
     subject { described_class }
     let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: { id: SecureRandom.uuid }) }
-    let(:request) { instance_double(Saml::Kit::AuthenticationRequest, id: Saml::Kit::Id.generate, issuer: FFaker::Internet.http_url, assertion_consumer_service_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, provider: nil, signed?: true, trusted?: true) }
+    let(:request) { instance_double(Saml::Kit::AuthenticationRequest, id: Xml::Kit::Id.generate, issuer: FFaker::Internet.http_url, assertion_consumer_service_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, provider: nil, signed?: true, trusted?: true) }
 
     it 'returns a Response' do
       xml = Saml::Kit::Response.build_xml(user, request)
spec/saml/fingerprint_spec.rb
@@ -1,27 +0,0 @@
-RSpec.describe Saml::Kit::Fingerprint do
-  describe "#sha" do
-    it 'returns the SHA256' do
-      certificate, _ = Saml::Kit::SelfSignedCertificate.new("password").create
-      x509 = OpenSSL::X509::Certificate.new(certificate)
-      sha256 = OpenSSL::Digest::SHA256.new.hexdigest(x509.to_der).upcase.scan(/../).join(":")
-
-      expect(described_class.new(certificate).algorithm(OpenSSL::Digest::SHA256)).to eql(sha256)
-    end
-
-    it 'returns the SHA1' do
-      certificate, _ = Saml::Kit::SelfSignedCertificate.new("password").create
-      x509 = OpenSSL::X509::Certificate.new(certificate)
-      sha1 = OpenSSL::Digest::SHA1.new.hexdigest(x509.to_der).upcase.scan(/../).join(":")
-
-      expect(described_class.new(certificate).algorithm(OpenSSL::Digest::SHA1)).to eql(sha1)
-    end
-  end
-
-  it 'produces correct hash keys' do
-    certificate, _ = Saml::Kit::SelfSignedCertificate.new("password").create
-    items = { }
-    items[described_class.new(certificate)] = "HI"
-    items[described_class.new(certificate)] = "BYE"
-    expect(items.keys.count).to eql(1)
-  end
-end
spec/saml/identity_provider_metadata_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Saml::Kit::IdentityProviderMetadata do
     it { expect(subject.single_logout_services).to be_empty }
     it do
       fingerprint = "9F:74:13:3B:BC:5A:7B:8B:2D:4F:8B:EF:1E:88:EB:D1:AE:BC:19:BF:CA:19:C6:2F:0F:4B:31:1D:68:98:B0:1B"
-      expect(subject.certificates).to match_array([Saml::Kit::Certificate.new(certificate, use: :signing)])
+      expect(subject.certificates).to match_array([::Xml::Kit::Certificate.new(certificate, use: :signing)])
       expect(subject.certificates.first.fingerprint.to_s).to eql(fingerprint)
     end
     it { expect(subject.attributes).to be_empty }
@@ -59,8 +59,8 @@ RSpec.describe Saml::Kit::IdentityProviderMetadata do
     end
     it do
       expect(subject.certificates).to match_array([
-        Saml::Kit::Certificate.new(signing_certificate, use: :signing),
-        Saml::Kit::Certificate.new(encryption_certificate, use: :encryption),
+        ::Xml::Kit::Certificate.new(signing_certificate, use: :signing),
+        ::Xml::Kit::Certificate.new(encryption_certificate, use: :encryption),
       ])
     end
     it { expect(subject.attributes).to be_present }
spec/saml/logout_request_spec.rb
@@ -112,10 +112,9 @@ RSpec.describe Saml::Kit::LogoutRequest do
     end
 
     it 'validates the schema of the request' do
-      id = Saml::Kit::Id.generate
-      configuration = Saml::Kit::Configuration.new
-      configuration.generate_key_pair_for(use: :signing)
-      signed_xml = Saml::Kit::Signatures.sign(configuration: configuration) do |xml, signature|
+      id = Xml::Kit::Id.generate
+      key_pair = ::Xml::Kit::KeyPair.generate(use: :signing)
+      signed_xml = ::Xml::Kit::Signatures.sign(key_pair: key_pair) do |xml, signature|
         xml.LogoutRequest ID: id do
           signature.template(id)
           xml.Fake do
spec/saml/metadata_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Saml::Kit::Metadata do
 
     it 'returns a composite' do
       xml = <<-XML
-<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{Saml::Kit::Id.generate}" entityID="#{FFaker::Internet.uri("https")}">
+<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{Xml::Kit::Id.generate}" entityID="#{FFaker::Internet.uri("https")}">
   <SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="#{Saml::Kit::Namespaces::PROTOCOL}">
     <SingleLogoutService Binding="#{Saml::Kit::Bindings::HTTP_POST}" Location="#{FFaker::Internet.uri("https")}"/>
     <NameIDFormat>#{Saml::Kit::Namespaces::PERSISTENT}</NameIDFormat>
spec/saml/response_spec.rb
@@ -1,6 +1,6 @@
 RSpec.describe Saml::Kit::Response do
   describe "#valid?" do
-    let(:request) { instance_double(Saml::Kit::AuthenticationRequest, id: Saml::Kit::Id.generate, issuer: FFaker::Internet.http_url, assertion_consumer_service_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, provider: nil, signed?: true, trusted?: true) }
+    let(:request) { instance_double(Saml::Kit::AuthenticationRequest, id: ::Xml::Kit::Id.generate, issuer: FFaker::Internet.http_url, assertion_consumer_service_url: FFaker::Internet.http_url, name_id_format: Saml::Kit::Namespaces::PERSISTENT, provider: nil, signed?: true, trusted?: true) }
     let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: { id: SecureRandom.uuid }) }
     let(:registry) { instance_double(Saml::Kit::DefaultRegistry) }
     let(:metadata) { instance_double(Saml::Kit::IdentityProviderMetadata) }
@@ -54,10 +54,9 @@ RSpec.describe Saml::Kit::Response do
     it 'validates the schema of the response' do
       allow(registry).to receive(:metadata_for).and_return(metadata)
       allow(metadata).to receive(:matches?).and_return(true)
-      id = Saml::Kit::Id.generate
-      configuration = Saml::Kit::Configuration.new
-      configuration.generate_key_pair_for(use: :signing)
-      signed_xml = Saml::Kit::Signatures.sign(configuration: configuration) do |xml, signature|
+      id = Xml::Kit::Id.generate
+      key_pair = ::Xml::Kit::KeyPair.generate(use: :signing)
+      signed_xml = ::Xml::Kit::Signatures.sign(key_pair: key_pair) do |xml, signature|
         xml.tag! "samlp:Response", "xmlns:samlp" => Saml::Kit::Namespaces::PROTOCOL, ID: id do
           signature.template(id)
           xml.Fake do
@@ -146,7 +145,7 @@ RSpec.describe Saml::Kit::Response do
       destination = FFaker::Internet.uri("https")
       raw_xml = <<-XML
 <?xml version="1.0"?>
-<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" ID="#{Saml::Kit::Id.generate}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{destination}" Consent="#{Saml::Kit::Namespaces::UNSPECIFIED}" InResponseTo="#{request.id}">
+<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" ID="#{Xml::Kit::Id.generate}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{destination}" Consent="#{Saml::Kit::Namespaces::UNSPECIFIED}" InResponseTo="#{request.id}">
   <Issuer xmlns="#{Saml::Kit::Namespaces::ASSERTION}">#{request.issuer}</Issuer>
   <samlp:Status>
     <samlp:StatusCode Value="#{Saml::Kit::Namespaces::RESPONDER_ERROR}"/>
@@ -160,11 +159,9 @@ RSpec.describe Saml::Kit::Response do
     end
 
     it 'is invalid when there are 2 assertions' do
-      id = Saml::Kit::Id.generate
+      id = Xml::Kit::Id.generate
       issuer = FFaker::Internet.uri("https")
-      configuration = Saml::Kit::Configuration.new do |config|
-        config.generate_key_pair_for(use: :signing)
-      end
+      key_pair = ::Xml::Kit::KeyPair.generate(use: :signing)
       response_options = {
         ID: id,
         Version: "2.0",
@@ -174,12 +171,12 @@ RSpec.describe Saml::Kit::Response do
         xmlns: Saml::Kit::Namespaces::PROTOCOL,
       }
       assertion_options = {
-        ID: Saml::Kit::Id.generate,
+        ID: Xml::Kit::Id.generate,
         IssueInstant: Time.now.iso8601,
         Version: "2.0",
         xmlns: Saml::Kit::Namespaces::ASSERTION,
       }
-      xml = Saml::Kit::Signatures.sign(configuration: configuration) do |xml, signature|
+      xml = ::Xml::Kit::Signatures.sign(key_pair: key_pair) do |xml, signature|
         xml.instruct!
         xml.Response response_options do
           xml.Issuer(issuer, xmlns: Saml::Kit::Namespaces::ASSERTION)
@@ -206,7 +203,7 @@ RSpec.describe Saml::Kit::Response do
               end
             end
           end
-          new_options = assertion_options.merge(ID: Saml::Kit::Id.generate)
+          new_options = assertion_options.merge(ID: Xml::Kit::Id.generate)
           xml.Assertion(new_options) do
             xml.Issuer issuer
             xml.Subject do
@@ -236,7 +233,7 @@ RSpec.describe Saml::Kit::Response do
 
   describe "#signed?" do
     let(:now) { Time.now.utc }
-    let(:id) { Saml::Kit::Id.generate }
+    let(:id) { Xml::Kit::Id.generate }
     let(:url) { FFaker::Internet.uri("https") }
 
     it 'returns true when the Assertion is signed' do
@@ -317,11 +314,11 @@ RSpec.describe Saml::Kit::Response do
 
   describe "#certificate" do
     let(:now) { Time.now.utc }
-    let(:id) { Saml::Kit::Id.generate }
+    let(:id) { Xml::Kit::Id.generate }
     let(:url) { FFaker::Internet.uri("https") }
     let(:certificate) do
-      Saml::Kit::Certificate.new(
-        Saml::Kit::SelfSignedCertificate.new("password").create[0],
+      ::Xml::Kit::Certificate.new(
+        ::Xml::Kit::SelfSignedCertificate.new("password").create[0],
         use: :signing
       )
     end
@@ -404,7 +401,7 @@ RSpec.describe Saml::Kit::Response do
   end
 
   describe "encrypted assertion" do
-    let(:id) { Saml::Kit::Id.generate }
+    let(:id) { Xml::Kit::Id.generate }
     let(:now) { Time.now.utc }
     let(:assertion_consumer_service_url) { FFaker::Internet.uri("https") }
     let(:password) { FFaker::Movie.title }
@@ -442,7 +439,7 @@ XML
     end
 
     it 'parses the encrypted assertion' do
-      certificate_pem, private_key_pem = Saml::Kit::SelfSignedCertificate.new(password).create
+      certificate_pem, private_key_pem = ::Xml::Kit::SelfSignedCertificate.new(password).create
       public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
       private_key = OpenSSL::PKey::RSA.new(private_key_pem, password)
 
@@ -455,7 +452,7 @@ XML
       encrypted = cipher.update(assertion) + cipher.final
 
       xml = <<-XML
-<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" xmlns:saml="#{Saml::Kit::Namespaces::ASSERTION}" ID="#{id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{assertion_consumer_service_url}" InResponseTo="#{Saml::Kit::Id.generate}">
+<samlp:Response xmlns:samlp="#{Saml::Kit::Namespaces::PROTOCOL}" xmlns:saml="#{Saml::Kit::Namespaces::ASSERTION}" ID="#{id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{assertion_consumer_service_url}" InResponseTo="#{Xml::Kit::Id.generate}">
   <saml:Issuer>#{FFaker::Internet.uri("https")}</saml:Issuer>
   <samlp:Status>
     <samlp:StatusCode Value="#{Saml::Kit::Namespaces::SUCCESS}"/>
@@ -489,7 +486,7 @@ XML
 
   describe "parsing" do
     let(:user) { double(:user, name_id_for: SecureRandom.uuid, assertion_attributes_for: attributes) }
-    let(:request) { double(:request, id: Saml::Kit::Id.generate, signed?: true, trusted?: true, provider: nil, assertion_consumer_service_url: FFaker::Internet.uri("https"), name_id_format: '', issuer: FFaker::Internet.uri("https")) }
+    let(:request) { double(:request, id: Xml::Kit::Id.generate, signed?: true, trusted?: true, provider: nil, assertion_consumer_service_url: FFaker::Internet.uri("https"), name_id_format: '', issuer: FFaker::Internet.uri("https")) }
     let(:attributes) { { name: 'mo' } }
 
     it 'returns the name id' do
spec/saml/service_provider_metadata_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Saml::Kit::ServiceProviderMetadata do
 
     it 'returns each of the certificates' do
       expected_certificates = Saml::Kit.configuration.certificates.map do |x|
-        Saml::Kit::Certificate.new(x.stripped, use: x.use)
+        ::Xml::Kit::Certificate.new(x.stripped, use: x.use)
       end
       expect(subject.certificates).to match_array(expected_certificates)
     end
@@ -104,7 +104,7 @@ RSpec.describe Saml::Kit::ServiceProviderMetadata do
     it 'is invalid when 0 ACS endpoints are specified' do
       xml = <<-XML
 <?xml version="1.0" encoding="UTF-8"?>
-<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{Saml::Kit::Id.generate}" entityID="#{entity_id}">
+<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{::Xml::Kit::Id.generate}" entityID="#{entity_id}">
   <SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="#{Saml::Kit::Namespaces::PROTOCOL}">
     <SingleLogoutService Binding="#{Saml::Kit::Bindings::HTTP_POST}" Location="#{FFaker::Internet.uri("https")}"/>
     <NameIDFormat>#{Saml::Kit::Namespaces::PERSISTENT}</NameIDFormat>
@@ -125,13 +125,13 @@ RSpec.describe Saml::Kit::ServiceProviderMetadata do
 
     it 'returns true when the fingerprint matches one of the signing certificates' do
       certificate = Hash.from_xml(subject.to_xml)['EntityDescriptor']['Signature']['KeyInfo']['X509Data']['X509Certificate']
-      fingerprint = Saml::Kit::Fingerprint.new(certificate)
+      fingerprint = ::Xml::Kit::Fingerprint.new(certificate)
       expect(subject.matches?(fingerprint)).to be_truthy
     end
 
     it 'returns false when the fingerprint does not match one of the signing certificates' do
-      certificate, _ = Saml::Kit::SelfSignedCertificate.new('password').create
-      fingerprint = Saml::Kit::Fingerprint.new(certificate)
+      certificate, _ = ::Xml::Kit::SelfSignedCertificate.new('password').create
+      fingerprint = ::Xml::Kit::Fingerprint.new(certificate)
       expect(subject.matches?(fingerprint)).to be_falsey
     end
   end
spec/saml/signatures_spec.rb
@@ -1,68 +0,0 @@
-RSpec.describe Saml::Kit::Signatures do
-  let(:configuration) do
-    config = Saml::Kit::Configuration.new
-    config.add_key_pair(certificate, private_key, passphrase: passphrase, use: :signing)
-    config
-  end
-
-  let(:reference_id) { Saml::Kit::Id.generate }
-  let(:rsa_key) { OpenSSL::PKey::RSA.new(2048) }
-  let(:public_key) { rsa_key.public_key }
-  let(:certificate) do
-    x = OpenSSL::X509::Certificate.new
-    x.subject = x.issuer = OpenSSL::X509::Name.parse("/C=CA/ST=Alberta/L=Calgary/O=Xsig/OU=Xsig/CN=Xsig")
-    x.not_before = Time.now
-    x.not_after = Time.now + 365 * 24 * 60 * 60
-    x.public_key = public_key
-    x.serial = 0x0
-    x.version = 2
-    x.sign(rsa_key, OpenSSL::Digest::SHA256.new)
-    x.to_pem
-  end
-  let(:private_key) { rsa_key.to_pem(OpenSSL::Cipher.new('des3'), passphrase) }
-  let(:passphrase) { "password" }
-
-  it 'generates a signature' do
-    options = {
-      "xmlns:samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
-      "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion",
-      ID: reference_id,
-    }
-    signed_xml = described_class.sign(configuration: configuration) do |xml, signature|
-      xml.tag!('samlp:AuthnRequest', options) do
-        signature.template(reference_id)
-        xml.tag!('saml:Issuer', "MyEntityID")
-      end
-    end
-    result = Hash.from_xml(signed_xml)
-
-    signature = result["AuthnRequest"]["Signature"]
-    expect(signature['xmlns']).to eql("http://www.w3.org/2000/09/xmldsig#")
-    expect(signature['SignedInfo']['CanonicalizationMethod']['Algorithm']).to eql('http://www.w3.org/2001/10/xml-exc-c14n#')
-    expect(signature['SignedInfo']['SignatureMethod']['Algorithm']).to eql("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")
-
-    expect(signature['SignedInfo']['Reference']['URI']).to eql("##{reference_id}")
-    expect(signature['SignedInfo']['Reference']['Transforms']['Transform']).to match_array([
-      { "Algorithm" => "http://www.w3.org/2000/09/xmldsig#enveloped-signature" },
-      { "Algorithm" => "http://www.w3.org/2001/10/xml-exc-c14n#" }
-    ])
-    expect(signature['SignedInfo']['Reference']['DigestMethod']['Algorithm']).to eql("http://www.w3.org/2001/04/xmlenc#sha256")
-    expected_certificate = certificate.gsub(/\n/, '').gsub(/-----BEGIN CERTIFICATE-----/, '').gsub(/-----END CERTIFICATE-----/, '')
-    expect(signature['KeyInfo']['X509Data']['X509Certificate']).to eql(expected_certificate)
-    expect(signature['SignedInfo']['Reference']['DigestValue']).to be_present
-    expect(signature['SignatureValue']).to be_present
-    expect(OpenSSL::X509::Certificate.new(Base64.decode64(signature['KeyInfo']['X509Data']['X509Certificate']))).to be_present
-  end
-
-  it 'does not add a signature' do
-    signed_xml = described_class.sign(configuration: Saml::Kit::Configuration.new) do |xml, signature|
-      xml.AuthnRequest do
-        signature.template(reference_id)
-        xml.Issuer "MyEntityID"
-      end
-    end
-    result = Hash.from_xml(signed_xml)
-    expect(result['AuthnRequest']).to be_present
-    expect(result["AuthnRequest"]["Signature"]).to be_nil
-  end
-end
spec/saml/xml_decryption_spec.rb
@@ -1,133 +0,0 @@
-RSpec.describe Saml::Kit::XmlDecryption do
-  describe "#decrypt" do
-    let(:secret) { FFaker::Movie.title }
-    let(:password) { FFaker::Movie.title }
-
-    it 'decrypts the data' do
-      certificate_pem, private_key_pem = Saml::Kit::SelfSignedCertificate.new(password).create
-      public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
-      private_key = OpenSSL::PKey::RSA.new(private_key_pem, password)
-
-      cipher = OpenSSL::Cipher.new('AES-128-CBC')
-      cipher.encrypt
-      key = cipher.random_key
-      iv = cipher.random_iv
-      encrypted = cipher.update(secret) + cipher.final
-
-      data = {
-        "EncryptedData"=> {
-          "xmlns:xenc"=>"http://www.w3.org/2001/04/xmlenc#",
-          "xmlns:dsig"=>"http://www.w3.org/2000/09/xmldsig#",
-          "Type"=>"http://www.w3.org/2001/04/xmlenc#Element",
-          "EncryptionMethod"=> {
-            "Algorithm"=>"http://www.w3.org/2001/04/xmlenc#aes128-cbc"
-          },
-          "KeyInfo"=> {
-            "xmlns:dsig"=>"http://www.w3.org/2000/09/xmldsig#",
-            "EncryptedKey"=> {
-              "EncryptionMethod"=>{
-                "Algorithm"=>"http://www.w3.org/2001/04/xmlenc#rsa-1_5"
-              },
-              "CipherData"=>{
-                "CipherValue"=> Base64.encode64(public_key.public_encrypt(key))
-              }
-            }
-          },
-          "CipherData"=>{
-            "CipherValue"=> Base64.encode64(iv + encrypted)
-          }
-        }
-      }
-      subject = described_class.new(configuration: double(private_keys: [private_key]))
-      decrypted = subject.decrypt(data)
-      expect(decrypted.strip).to eql(secret)
-    end
-
-    it 'attemps to decrypt with each encryption keypair' do
-      certificate_pem, private_key_pem = Saml::Kit::SelfSignedCertificate.new(password).create
-      public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
-      private_key = OpenSSL::PKey::RSA.new(private_key_pem, password)
-
-      cipher = OpenSSL::Cipher.new('AES-128-CBC')
-      cipher.encrypt
-      key = cipher.random_key
-      iv = cipher.random_iv
-      encrypted = cipher.update(secret) + cipher.final
-
-      data = {
-        "EncryptedData"=> {
-          "xmlns:xenc"=>"http://www.w3.org/2001/04/xmlenc#",
-          "xmlns:dsig"=>"http://www.w3.org/2000/09/xmldsig#",
-          "Type"=>"http://www.w3.org/2001/04/xmlenc#Element",
-          "EncryptionMethod"=> {
-            "Algorithm"=>"http://www.w3.org/2001/04/xmlenc#aes128-cbc"
-          },
-          "KeyInfo"=> {
-            "xmlns:dsig"=>"http://www.w3.org/2000/09/xmldsig#",
-            "EncryptedKey"=> {
-              "EncryptionMethod"=>{
-                "Algorithm"=>"http://www.w3.org/2001/04/xmlenc#rsa-1_5"
-              },
-              "CipherData"=>{
-                "CipherValue"=> Base64.encode64(public_key.public_encrypt(key))
-              }
-            }
-          },
-          "CipherData"=>{
-            "CipherValue"=> Base64.encode64(iv + encrypted)
-          }
-        }
-      }
-
-      _, other_private_key_pem = Saml::Kit::SelfSignedCertificate.new(password).create
-      other_private_key = OpenSSL::PKey::RSA.new(other_private_key_pem, password)
-
-      subject = described_class.new(configuration: double(private_keys: [other_private_key, private_key]))
-      decrypted = subject.decrypt(data)
-      expect(decrypted.strip).to eql(secret)
-    end
-
-    it 'raise an error when it cannot decrypt the data' do
-      certificate_pem, _ = Saml::Kit::SelfSignedCertificate.new(password).create
-      public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
-
-      cipher = OpenSSL::Cipher.new('AES-128-CBC')
-      cipher.encrypt
-      key = cipher.random_key
-      iv = cipher.random_iv
-      encrypted = cipher.update(secret) + cipher.final
-
-      data = {
-        "EncryptedData"=> {
-          "xmlns:xenc"=>"http://www.w3.org/2001/04/xmlenc#",
-          "xmlns:dsig"=>"http://www.w3.org/2000/09/xmldsig#",
-          "Type"=>"http://www.w3.org/2001/04/xmlenc#Element",
-          "EncryptionMethod"=> {
-            "Algorithm"=>"http://www.w3.org/2001/04/xmlenc#aes128-cbc"
-          },
-          "KeyInfo"=> {
-            "xmlns:dsig"=>"http://www.w3.org/2000/09/xmldsig#",
-            "EncryptedKey"=> {
-              "EncryptionMethod"=>{
-                "Algorithm"=>"http://www.w3.org/2001/04/xmlenc#rsa-1_5"
-              },
-              "CipherData"=>{
-                "CipherValue"=> Base64.encode64(public_key.public_encrypt(key))
-              }
-            }
-          },
-          "CipherData"=>{
-            "CipherValue"=> Base64.encode64(iv + encrypted)
-          }
-        }
-      }
-
-      new_private_key_pem = Saml::Kit::SelfSignedCertificate.new(password).create[1]
-      new_private_key = OpenSSL::PKey::RSA.new(new_private_key_pem, password)
-      subject = described_class.new(configuration: double(private_keys: [new_private_key]))
-      expect do
-        subject.decrypt(data)
-      end.to raise_error(OpenSSL::PKey::RSAError)
-    end
-  end
-end
spec/saml/xml_spec.rb
@@ -1,58 +0,0 @@
-RSpec.describe Saml::Kit::Xml do
-  describe "#valid_signature?" do
-    let(:login_url) { "https://#{FFaker::Internet.domain_name}/login" }
-    let(:logout_url) { "https://#{FFaker::Internet.domain_name}/logout" }
-    let(:configuration) do
-      configuration = Saml::Kit::Configuration.new
-      configuration.generate_key_pair_for(use: :signing)
-      configuration
-    end
-
-    let(:signed_xml) do
-      Saml::Kit::ServiceProviderMetadata.build(configuration: configuration) do |builder|
-        builder.entity_id = FFaker::Movie.title
-        builder.add_assertion_consumer_service(login_url, binding: :http_post)
-        builder.add_assertion_consumer_service(login_url, binding: :http_redirect)
-        builder.add_single_logout_service(logout_url, binding: :http_post)
-        builder.add_single_logout_service(logout_url, binding: :http_redirect)
-      end.to_xml
-    end
-
-    it 'returns true, when the digest and signature is valid' do
-      subject = described_class.new(signed_xml)
-      expect(subject).to be_valid
-    end
-
-    it 'returns false, when the SHA1 digest is not valid' do
-      subject = described_class.new(signed_xml.gsub("EntityDescriptor", "uhoh"))
-      expect(subject).to_not be_valid
-      expect(subject.errors[:digest_value]).to be_present
-    end
-
-    it 'it is invalid when digest is incorrect' do
-      old_digest = Hash.from_xml(signed_xml)['EntityDescriptor']['Signature']['SignedInfo']['Reference']['DigestValue']
-      subject = described_class.new(signed_xml.gsub(old_digest, 'sabotage'))
-      expect(subject).to_not be_valid
-      expect(subject.errors[:digest_value]).to be_present
-    end
-
-    it 'returns false, when the signature is invalid' do
-      old_signature = Hash.from_xml(signed_xml)['EntityDescriptor']['Signature']['SignatureValue']
-      signed_xml.gsub!(old_signature, 'sabotage')
-      subject = described_class.new(signed_xml)
-      expect(subject).to_not be_valid
-      expect(subject.errors[:signature]).to be_present
-    end
-
-    it 'is valid' do
-      configuration = Saml::Kit::Configuration.new do |config|
-        5.times { config.generate_key_pair_for(use: :signing) }
-      end
-      signed_xml = Saml::Kit::Metadata.build_xml(configuration: configuration) do |builder|
-        builder.build_identity_provider
-        builder.build_service_provider
-      end
-      expect(described_class.new(signed_xml)).to be_valid
-    end
-  end
-end
spec/support/matchers/have_xml.rb
@@ -1,6 +1,13 @@
 RSpec::Matchers.define :have_xpath do |xpath|
   match do |actual|
-    xml_document(actual).xpath(xpath, Saml::Kit::Xml::NAMESPACES).any?
+    namespaces = {
+      "NameFormat": Saml::Kit::Namespaces::ATTR_SPLAT,
+      "ds": ::Xml::Kit::Namespaces::XMLDSIG,
+      "md": Saml::Kit::Namespaces::METADATA,
+      "saml": Saml::Kit::Namespaces::ASSERTION,
+      "samlp": Saml::Kit::Namespaces::PROTOCOL,
+    }
+    xml_document(actual).xpath(xpath, namespaces).any?
   end
 
   failure_message do |actual|
spec/spec_helper.rb
@@ -8,7 +8,7 @@ require "active_support/testing/time_helpers"
 require "ffaker"
 require "webmock/rspec"
 
-Saml::Kit.configuration.logger.level = Logger::FATAL
+Saml::Kit.configuration.logger.level = Xml::Kit.logger.level = Logger::FATAL
 
 Dir[File.join(Dir.pwd, 'spec/support/**/*.rb')].each { |f| require f }
 RSpec.configure do |config|
saml-kit.gemspec
@@ -24,10 +24,8 @@ Gem::Specification.new do |spec|
   spec.require_paths = ["lib"]
 
   spec.add_dependency "activemodel", ">= 4.2.0"
-  spec.add_dependency "builder", "~> 3.2"
   spec.add_dependency "nokogiri", "~> 1.8"
-  spec.add_dependency "tilt", "~> 2.0"
-  spec.add_dependency "xmldsig", "~> 0.6"
+  spec.add_dependency "xml-kit", "0.1.0"
   spec.add_development_dependency "bundler", "~> 1.15"
   spec.add_development_dependency "ffaker", "~> 2.7"
   spec.add_development_dependency "rake", "~> 10.0"