diff --git a/lib/saml_idp/assertion_builder.rb b/lib/saml_idp/assertion_builder.rb index 8e0170fa..99c17ad3 100644 --- a/lib/saml_idp/assertion_builder.rb +++ b/lib/saml_idp/assertion_builder.rb @@ -18,6 +18,9 @@ class AssertionBuilder attr_accessor :session_expiry attr_accessor :name_id_formats_opts attr_accessor :asserted_attributes_opts + attr_accessor :x509_certificate + attr_accessor :secret_key + attr_accessor :password delegate :config, to: :SamlIdp @@ -34,7 +37,10 @@ def initialize( encryption_opts=nil, session_expiry=nil, name_id_formats_opts = nil, - asserted_attributes_opts = nil + asserted_attributes_opts = nil, + x509_certificate = nil, + secret_key = nil, + password = nil ) self.reference_id = reference_id self.issuer_uri = issuer_uri @@ -49,6 +55,9 @@ def initialize( self.session_expiry = session_expiry.nil? ? config.session_expiry : session_expiry self.name_id_formats_opts = name_id_formats_opts self.asserted_attributes_opts = asserted_attributes_opts + self.x509_certificate = x509_certificate + self.secret_key = secret_key + self.password = password end def fresh diff --git a/lib/saml_idp/controller.rb b/lib/saml_idp/controller.rb index e2bf25e7..9604df49 100644 --- a/lib/saml_idp/controller.rb +++ b/lib/saml_idp/controller.rb @@ -65,6 +65,9 @@ def encode_authn_response(principal, opts = {}) asserted_attributes_opts = opts[:attributes] || nil signed_assertion_opts = opts[:signed_assertion] || true compress_opts = opts[:compress] || false + x509_certificate_opts = opts[:x509_certificate] || nil + secret_key_opts = opts[:secret_key] || nil + password_opts = opts[:password] || nil SamlResponse.new( reference_id, @@ -83,7 +86,10 @@ def encode_authn_response(principal, opts = {}) asserted_attributes_opts, signed_message_opts, signed_assertion_opts, - compress_opts + compress_opts, + x509_certificate_opts, + secret_key_opts, + password_opts ).build end @@ -93,7 +99,10 @@ def encode_logout_response(_principal, opts = {}) (opts[:issuer_uri] || issuer_uri), saml_logout_url, saml_request_id, - (opts[:algorithm] || algorithm || default_algorithm) + (opts[:algorithm] || algorithm || default_algorithm), + opts[:x509_certificate] || nil, + opts[:secret_key] || nil, + opts[:password] || nil ).signed end diff --git a/lib/saml_idp/logout_builder.rb b/lib/saml_idp/logout_builder.rb index 7d049dd6..407d3e75 100644 --- a/lib/saml_idp/logout_builder.rb +++ b/lib/saml_idp/logout_builder.rb @@ -7,12 +7,26 @@ class LogoutBuilder attr_accessor :issuer_uri attr_accessor :saml_slo_url attr_accessor :algorithm + attr_accessor :x509_certificate + attr_accessor :secret_key + attr_accessor :password - def initialize(response_id, issuer_uri, saml_slo_url, algorithm) + def initialize( + response_id, + issuer_uri, + saml_slo_url, + algorithm, + x509_certificate = nil, + secret_key = nil, + password = nil + ) self.response_id = response_id self.issuer_uri = issuer_uri self.saml_slo_url = saml_slo_url self.algorithm = algorithm + self.x509_certificate = x509_certificate + self.secret_key = secret_key + self.password = password end # this is an abstract base class. diff --git a/lib/saml_idp/logout_request_builder.rb b/lib/saml_idp/logout_request_builder.rb index b75f9315..4754305e 100644 --- a/lib/saml_idp/logout_request_builder.rb +++ b/lib/saml_idp/logout_request_builder.rb @@ -3,8 +3,17 @@ module SamlIdp class LogoutRequestBuilder < LogoutBuilder attr_accessor :name_id - def initialize(response_id, issuer_uri, saml_slo_url, name_id, algorithm) - super(response_id, issuer_uri, saml_slo_url, algorithm) + def initialize( + response_id, + issuer_uri, + saml_slo_url, + name_id, + algorithm, + x509_certificate = nil, + secret_key = nil, + password = nil + ) + super(response_id, issuer_uri, saml_slo_url, algorithm, x509_certificate, secret_key, password) self.name_id = name_id end diff --git a/lib/saml_idp/logout_response_builder.rb b/lib/saml_idp/logout_response_builder.rb index fd14116b..bb1a24f3 100644 --- a/lib/saml_idp/logout_response_builder.rb +++ b/lib/saml_idp/logout_response_builder.rb @@ -3,8 +3,17 @@ module SamlIdp class LogoutResponseBuilder < LogoutBuilder attr_accessor :saml_request_id - def initialize(response_id, issuer_uri, saml_slo_url, saml_request_id, algorithm) - super(response_id, issuer_uri, saml_slo_url, algorithm) + def initialize( + response_id, + issuer_uri, + saml_slo_url, + saml_request_id, + algorithm, + x509_certificate = nil, + secret_key = nil, + password = nil + ) + super(response_id, issuer_uri, saml_slo_url, algorithm, x509_certificate, secret_key, password) self.saml_request_id = saml_request_id end diff --git a/lib/saml_idp/metadata_builder.rb b/lib/saml_idp/metadata_builder.rb index 9a4afb92..d4e5b7f4 100644 --- a/lib/saml_idp/metadata_builder.rb +++ b/lib/saml_idp/metadata_builder.rb @@ -7,11 +7,21 @@ class MetadataBuilder include Algorithmable include Signable attr_accessor :configurator + attr_accessor :x509_certificate + attr_accessor :secret_key + attr_accessor :password def initialize(configurator = SamlIdp.config) self.configurator = configurator end + def customized(x509_certificate = nil, secret_key = nil, password = nil) + self.x509_certificate = x509_certificate + self.secret_key = secret_key + self.password = password + self + end + def fresh builder = Builder::XmlMarkup.new generated_reference_id do @@ -58,7 +68,7 @@ def build_key_descriptor(el) el.KeyDescriptor use: "signing" do |key_descriptor| key_descriptor.KeyInfo xmlns: Saml::XML::Namespaces::SIGNATURE do |key_info| key_info.X509Data do |x509| - x509.X509Certificate x509_certificate + x509.X509Certificate get_x509_certificate end end end @@ -151,14 +161,6 @@ def raw_algorithm end private :raw_algorithm - def x509_certificate - SamlIdp.config.x509_certificate - .to_s - .gsub(/-----BEGIN CERTIFICATE-----/,"") - .gsub(/-----END CERTIFICATE-----/,"") - .gsub(/\n/, "") - end - %w[ support_email organization_name diff --git a/lib/saml_idp/response_builder.rb b/lib/saml_idp/response_builder.rb index 0686cd9b..c9b64472 100644 --- a/lib/saml_idp/response_builder.rb +++ b/lib/saml_idp/response_builder.rb @@ -11,16 +11,32 @@ class ResponseBuilder attr_accessor :saml_request_id attr_accessor :assertion_and_signature attr_accessor :raw_algorithm + attr_accessor :x509_certificate + attr_accessor :secret_key + attr_accessor :password alias_method :reference_id, :response_id - def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature, raw_algorithm) + def initialize( + response_id, + issuer_uri, + saml_acs_url, + saml_request_id, + assertion_and_signature, + raw_algorithm, + x509_certificate = nil, + secret_key = nil, + password = nil + ) self.response_id = response_id self.issuer_uri = issuer_uri self.saml_acs_url = saml_acs_url self.saml_request_id = saml_request_id self.assertion_and_signature = assertion_and_signature self.raw_algorithm = raw_algorithm + self.x509_certificate = x509_certificate + self.secret_key = secret_key + self.password = password end def encoded(signed_message: false, compress: false) diff --git a/lib/saml_idp/saml_response.rb b/lib/saml_idp/saml_response.rb index fdc327a5..f0247d93 100644 --- a/lib/saml_idp/saml_response.rb +++ b/lib/saml_idp/saml_response.rb @@ -14,6 +14,7 @@ class SamlResponse attr_accessor :algorithm attr_accessor :secret_key attr_accessor :x509_certificate + attr_accessor :password attr_accessor :authn_context_classref attr_accessor :expiry attr_accessor :encryption_opts @@ -41,7 +42,10 @@ def initialize( asserted_attributes_opts = nil, signed_message_opts = false, signed_assertion_opts = true, - compression_opts = false + compression_opts = false, + x509_certificate_opts = nil, + secret_key_opts = nil, + password_opts = nil ) self.reference_id = reference_id @@ -65,6 +69,9 @@ def initialize( self.name_id_formats_opts = name_id_formats_opts self.asserted_attributes_opts = asserted_attributes_opts self.compression_opts = compression_opts + self.x509_certificate = x509_certificate_opts + self.secret_key = secret_key_opts + self.password = password_opts end def build @@ -92,7 +99,17 @@ def encoded_message private :encoded_message def response_builder - ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion, algorithm) + ResponseBuilder.new( + response_id, + issuer_uri, + saml_acs_url, + saml_request_id, + signed_assertion, + algorithm, + x509_certificate, + secret_key, + password + ) end private :response_builder @@ -110,7 +127,10 @@ def assertion_builder encryption_opts, session_expiry, name_id_formats_opts, - asserted_attributes_opts + asserted_attributes_opts, + x509_certificate, + secret_key, + password end private :assertion_builder end diff --git a/lib/saml_idp/signable.rb b/lib/saml_idp/signable.rb index 0a330a18..da71d130 100644 --- a/lib/saml_idp/signable.rb +++ b/lib/saml_idp/signable.rb @@ -65,12 +65,12 @@ def sign? private :sign? def signature - SignatureBuilder.new(signed_info_builder).raw + SignatureBuilder.new(signed_info_builder, get_x509_certificate).raw end private :signature def signed_info_builder - SignedInfoBuilder.new(get_reference_id, get_digest, get_algorithm) + SignedInfoBuilder.new(get_reference_id, get_digest, get_algorithm, get_secret_key, get_password) end private :signed_info_builder @@ -101,6 +101,26 @@ def noko_raw end private :noko_raw + def get_x509_certificate + x509_certificate = send(self.class.x509_certificate_method).presence || SamlIdp.config.x509_certificate + x509_certificate + .to_s + .gsub(/-----BEGIN CERTIFICATE-----/,"") + .gsub(/-----END CERTIFICATE-----/,"") + .gsub(/\n/, "") + end + private :get_x509_certificate + + def get_secret_key + send(self.class.secret_key_method).presence || SamlIdp.config.secret_key + end + private :get_secret_key + + def get_password + send(self.class.password_method).presence || SamlIdp.config.password + end + private :get_password + def digest # Make it check for inclusive at some point (https://github.com/onelogin/ruby-saml/blob/master/lib/xml_security.rb#L159) inclusive_namespaces = [] @@ -125,6 +145,9 @@ def self.module_method(name, default = nil) module_method :digest module_method :algorithm module_method :reference_id + module_method :x509_certificate + module_method :secret_key + module_method :password end end end diff --git a/lib/saml_idp/signature_builder.rb b/lib/saml_idp/signature_builder.rb index 83183f23..30df7e8f 100644 --- a/lib/saml_idp/signature_builder.rb +++ b/lib/saml_idp/signature_builder.rb @@ -2,9 +2,11 @@ module SamlIdp class SignatureBuilder attr_accessor :signed_info_builder + attr_accessor :x509_certificate - def initialize(signed_info_builder) + def initialize(signed_info_builder, x509_certificate) self.signed_info_builder = signed_info_builder + self.x509_certificate = x509_certificate end def raw @@ -20,15 +22,6 @@ def raw end end - def x509_certificate - SamlIdp.config.x509_certificate - .to_s - .gsub(/-----BEGIN CERTIFICATE-----/,"") - .gsub(/-----END CERTIFICATE-----/,"") - .gsub(/\n/, "") - end - private :x509_certificate - def signed_info signed_info_builder.raw end diff --git a/lib/saml_idp/signed_info_builder.rb b/lib/saml_idp/signed_info_builder.rb index 81380666..bd4c0225 100644 --- a/lib/saml_idp/signed_info_builder.rb +++ b/lib/saml_idp/signed_info_builder.rb @@ -22,11 +22,15 @@ class SignedInfoBuilder attr_accessor :reference_id attr_accessor :digest_value attr_accessor :raw_algorithm + attr_accessor :secret_key + attr_accessor :password - def initialize(reference_id, digest_value, raw_algorithm) + def initialize(reference_id, digest_value, raw_algorithm, secret_key, password) self.reference_id = reference_id self.digest_value = digest_value self.raw_algorithm = raw_algorithm + self.secret_key = secret_key + self.password = password end def raw @@ -64,16 +68,6 @@ def clean_algorithm_name end private :clean_algorithm_name - def secret_key - SamlIdp.config.secret_key - end - private :secret_key - - def password - SamlIdp.config.password - end - private :password - def encoded key = OpenSSL::PKey::RSA.new(secret_key, password) Base64.strict_encode64(key.sign(algorithm.new, raw)) diff --git a/spec/lib/saml_idp/saml_response_spec.rb b/spec/lib/saml_idp/saml_response_spec.rb index a9e82151..72120932 100644 --- a/spec/lib/saml_idp/saml_response_spec.rb +++ b/spec/lib/saml_idp/saml_response_spec.rb @@ -10,7 +10,7 @@ module SamlIdp let(:saml_acs_url) { "localhost/acs" } let(:algorithm) { :sha1 } let(:secret_key) { Default::SECRET_KEY } - let(:x509_certificate) { Default::X509_CERTIFICATE } + let(:default_x509_certificate) { Default::X509_CERTIFICATE } let(:xauthn) { Default::X509_CERTIFICATE } let(:authn_context_classref) { Saml::XML::Namespaces::AuthnContext::ClassRef::PASSWORD diff --git a/spec/lib/saml_idp/signable_spec.rb b/spec/lib/saml_idp/signable_spec.rb index cd9a90ae..e2d9b595 100644 --- a/spec/lib/saml_idp/signable_spec.rb +++ b/spec/lib/saml_idp/signable_spec.rb @@ -21,6 +21,18 @@ def digest def algorithm OpenSSL::Digest::SHA1 end + + def x509_certificate + SamlIdp::Default::X509_CERTIFICATE + end + + def secret_key + SamlIdp::Default::SECRET_KEY + end + + def password + "password" + end end module SamlIdp diff --git a/spec/lib/saml_idp/signature_builder_spec.rb b/spec/lib/saml_idp/signature_builder_spec.rb index ff28575a..294f1448 100644 --- a/spec/lib/saml_idp/signature_builder_spec.rb +++ b/spec/lib/saml_idp/signature_builder_spec.rb @@ -4,8 +4,10 @@ module SamlIdp let(:signed) { "jvEbD/rsiPKmoXy7Lhm+FGn88NPGlap4EcPZ2fvjBnk03YESs87FXAIiZZEzN5xq4sBZksUmZe2bV3rrr9sxQNgQawmrrvr66ot7cJiv0ETFArr6kQIZaR5g/V0M4ydxvrfefp6cQVI0hXvmxi830pq0tISiO4J7tyBNX/kvhZk=" } let(:raw_info) { "em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=" } let(:signed_info) { double raw: raw_info, signed: signed } + let(:x509_certificate) { "MIIDqzCCAxSgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UEBxMGU3lkbmV5MQwwCgYDVQQKDANQSVQxCTAHBgNVBAsMADEYMBYGA1UEAwwPbGF3cmVuY2VwaXQuY29tMSUwIwYJKoZIhvcNAQkBDBZsYXdyZW5jZS5waXRAZ21haWwuY29tMB4XDTEyMDQyODAyMjIyOFoXDTMyMDQyMzAyMjIyOFowgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuBywPNlC1FopGLYfF96SotiK8Nj6/nW084O4omRMifzy7x955RLEy673q2aiJNB3LvE6Xvkt9cGtxtNoOXw1g2UvHKpldQbr6bOEjLNeDNW7j0ob+JrRvAUOK9CRgdyw5MC6lwqVQQ5C1DnaT/2fSBFjasBFTR24dEpfTy8HfKECAwEAAaOCASUwggEhMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgUgMB0GA1UdDgQWBBQNBGmmt3ytKpcJaBaYNbnyU2xkazATBgNVHSUEDDAKBggrBgEFBQcDATAdBglghkgBhvhCAQ0EEBYOVGVzdCBYNTA5IGNlcnQwgbMGA1UdIwSBqzCBqIAUDQRpprd8rSqXCWgWmDW58lNsZGuhgYykgYkwgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbYIBATANBgkqhkiG9w0BAQsFAAOBgQAEcVUPBX7uZmzqZJfy+tUPOT5ImNQj8VE2lerhnFjnGPHmHIqhpzgnwHQujJfs/a309Wm5qwcCaC1eO5cWjcG0x3OjdllsgYDatl5GAumtBx8J3NhWRqNUgitCIkQlxHIwUfgQaCushYgDDL5YbIQa++egCgpIZ+T0Dj5oRew//A==" } subject { described_class.new( - signed_info + signed_info, + x509_certificate ) } before do diff --git a/spec/lib/saml_idp/signed_info_builder_spec.rb b/spec/lib/saml_idp/signed_info_builder_spec.rb index 4685d39a..37cf871b 100644 --- a/spec/lib/saml_idp/signed_info_builder_spec.rb +++ b/spec/lib/saml_idp/signed_info_builder_spec.rb @@ -4,10 +4,14 @@ module SamlIdp let(:reference_id) { "abc" } let(:digest) { "em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=" } let(:algorithm) { :sha256 } + let(:secret_key) { SamlIdp::Default::SECRET_KEY } + let(:password) { nil } subject { described_class.new( reference_id, digest, - algorithm + algorithm, + secret_key, + password ) } before do