Commit 03c28fe
Changed files (11)
lib
saml
spec
lib/saml/kit/builders/templates/xml_signature.builder
@@ -14,7 +14,7 @@ xml.Signature "xmlns" => Saml::Kit::Namespaces::XMLDSIG do
xml.SignatureValue ""
xml.KeyInfo do
xml.X509Data do
- xml.X509Certificate x509_certificate
+ xml.X509Certificate certificate.stripped
end
end
end
lib/saml/kit/builders/xml_signature.rb
@@ -19,12 +19,12 @@ module Saml
attr_reader :embed_signature, :configuration
attr_reader :reference_id
- attr_reader :x509_certificate
+ attr_reader :certificate
- def initialize(reference_id, configuration:)
+ def initialize(reference_id, configuration:, certificate: )
@configuration = configuration
@reference_id = reference_id
- @x509_certificate = configuration.certificates(use: :signing).last.stripped
+ @certificate = certificate
end
def signature_method
lib/saml/kit/authentication_request.rb
@@ -41,9 +41,10 @@ module Saml
# Generate a Response for a specific user.
# @param user [Object] this is a custom user object that can be used for generating a nameid and assertion attributes.
# @param binding [Symbol] the SAML binding to use `:http_post` or `:http_redirect`.
- def response_for(user, binding:, relay_state: nil)
+ # @param configuration [Saml::Kit::Configuration] the configuration to use to build the response.
+ def response_for(user, binding:, relay_state: nil, configuration: Saml::Kit.configuration)
response_binding = provider.assertion_consumer_service_for(binding: binding)
- builder = Saml::Kit::Response.builder(user, self) do |x|
+ builder = Saml::Kit::Response.builder(user, self, configuration: configuration) do |x|
x.embed_signature = provider.want_assertions_signed
yield x if block_given?
end
lib/saml/kit/certificate.rb
@@ -35,7 +35,7 @@ module Saml
end
def ==(other)
- self.to_s == other.to_s
+ self.fingerprint == other.fingerprint
end
def eql?(other)
@@ -51,7 +51,7 @@ module Saml
end
def to_h
- { use: @use, x509: @value }
+ { use: @use, fingerprint: fingerprint.to_s }
end
def inspect
lib/saml/kit/key_pair.rb
@@ -3,15 +3,20 @@ module Saml
class KeyPair # :nodoc:
attr_reader :certificate, :private_key, :use
- def initialize(certificate, private_key, password, 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, password)
+ @private_key = OpenSSL::PKey::RSA.new(private_key, passphrase)
end
def for?(use)
@use == use
end
+
+ 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/self_signed_certificate.rb
@@ -3,8 +3,8 @@ module Saml
class SelfSignedCertificate
SUBJECT="/C=CA/ST=Alberta/L=Calgary/O=SamlKit/OU=SamlKit/CN=SamlKit"
- def initialize(password)
- @password = password
+ def initialize(passphrase)
+ @passphrase = passphrase
end
def create
@@ -20,7 +20,7 @@ module Saml
certificate.sign(rsa_key, OpenSSL::Digest::SHA256.new)
[
certificate.to_pem,
- rsa_key.to_pem(OpenSSL::Cipher.new('AES-256-CBC'), @password)
+ rsa_key.to_pem(OpenSSL::Cipher.new('AES-256-CBC'), @passphrase)
]
end
end
lib/saml/kit/signatures.rb
@@ -9,16 +9,22 @@ module Saml
@configuration = configuration
end
+ def sign_with(key_pair)
+ @certificate = key_pair.certificate
+ @private_key = key_pair.private_key
+ end
+
# @!visibility private
def build(reference_id)
return nil unless configuration.sign?
- Saml::Kit::Builders::XmlSignature.new(reference_id, configuration: configuration)
+ certificate = @certificate || configuration.certificates(use: :signing).last
+ 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 = configuration.private_keys(use: :signing).last
+ private_key = @private_key || configuration.private_keys(use: :signing).last
Xmldsig::SignedDocument.new(raw_xml).sign(private_key)
end
lib/saml/kit/templatable.rb
@@ -17,6 +17,10 @@ module Saml
render(signatures.build(reference_id), xml: xml)
end
+ def sign_with(key_pair)
+ signatures.sign_with(key_pair)
+ end
+
def sign?
embed_signature.nil? ? configuration.sign? : embed_signature && configuration.sign?
end
spec/saml/builders/response_spec.rb
@@ -145,4 +145,26 @@ RSpec.describe Saml::Kit::Builders::Response do
expect(result.assertion).to be_encrypted
end
end
+
+ describe ".build" do
+ let(:configuration) do
+ Saml::Kit::Configuration.new do |config|
+ config.issuer = issuer
+ config.generate_key_pair_for(use: :signing)
+ config.generate_key_pair_for(use: :signing)
+ config.generate_key_pair_for(use: :signing)
+ end
+ end
+
+ it 'signs the response with a specific certificate' do
+ key_pair = configuration.key_pairs(use: :signing)[1]
+ subject.embed_signature = true
+ subject.sign_with(key_pair)
+
+ result = subject.build
+
+ expect(result).to be_signed
+ expect(result.signature.certificate).to eql(key_pair.certificate)
+ end
+ end
end
spec/saml/authentication_request_spec.rb
@@ -189,5 +189,19 @@ RSpec.describe Saml::Kit::AuthenticationRequest do
response = provider.assertion_consumer_service_for(binding: :http_post).deserialize(saml_params)
expect(response).to be_instance_of(Saml::Kit::Response)
end
+
+ it 'serializes a response with the specified signing certificate' do
+ allow(subject).to receive(:provider).and_return(provider)
+ configuration = Saml::Kit::Configuration.new do |config|
+ config.generate_key_pair_for(use: :signing)
+ end
+ key_pair = configuration.key_pairs(use: :signing).first
+ url, saml_params = subject.response_for(user, binding: :http_post, configuration: configuration) do |builder|
+ builder.sign_with(key_pair)
+ end
+
+ response = provider.assertion_consumer_service_for(binding: :http_post).deserialize(saml_params)
+ expect(response).to be_instance_of(Saml::Kit::Response)
+ end
end
end
spec/saml/composite_metadata_spec.rb
@@ -9,6 +9,10 @@ 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(:xml) do
<<-XML
<EntityDescriptor xmlns="#{Saml::Kit::Namespaces::METADATA}" ID="#{Saml::Kit::Id.generate}" entityID="#{entity_id}">
@@ -16,14 +20,14 @@ RSpec.describe Saml::Kit::CompositeMetadata do
<KeyDescriptor use="signing">
<KeyInfo xmlns="#{Saml::Kit::Namespaces::XMLDSIG}">
<X509Data>
- <X509Certificate>SP-Signing-Certificate</X509Certificate>
+ <X509Certificate>#{sp_signing_certificate.stripped}</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
<KeyDescriptor use="encryption">
<KeyInfo xmlns="#{Saml::Kit::Namespaces::XMLDSIG}">
<X509Data>
- <X509Certificate>SP-Encryption-Certificate</X509Certificate>
+ <X509Certificate>#{sp_encryption_certificate.stripped}</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
@@ -35,14 +39,14 @@ RSpec.describe Saml::Kit::CompositeMetadata do
<KeyDescriptor use="signing">
<KeyInfo xmlns="#{Saml::Kit::Namespaces::XMLDSIG}">
<X509Data>
- <X509Certificate>IDP-Signing-Certificate</X509Certificate>
+ <X509Certificate>#{idp_signing_certificate.stripped}</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
<KeyDescriptor use="encryption">
<KeyInfo xmlns="#{Saml::Kit::Namespaces::XMLDSIG}">
<X509Data>
- <X509Certificate>IDP-Encryption-Certificate</X509Certificate>
+ <X509Certificate>#{idp_encryption_certificate.stripped}</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
@@ -99,23 +103,23 @@ RSpec.describe Saml::Kit::CompositeMetadata do
it { expect(subject.name_id_formats).to match_array([Saml::Kit::Namespaces::PERSISTENT]) }
it do
expect(subject.certificates).to match_array([
- Saml::Kit::Certificate.new('SP-Signing-Certificate', use: :signing),
- Saml::Kit::Certificate.new('SP-Encryption-Certificate', use: :encryption),
- Saml::Kit::Certificate.new('IDP-Signing-Certificate', use: :signing),
- Saml::Kit::Certificate.new('IDP-Encryption-Certificate', use: :encryption),
+ sp_signing_certificate,
+ sp_encryption_certificate,
+ idp_signing_certificate,
+ idp_encryption_certificate,
])
end
it do
expect(subject.encryption_certificates).to match_array([
- Saml::Kit::Certificate.new('SP-Encryption-Certificate', use: :encryption),
- Saml::Kit::Certificate.new('IDP-Encryption-Certificate', use: :encryption),
+ sp_encryption_certificate,
+ idp_encryption_certificate,
])
end
it do
expect(subject.signing_certificates).to match_array([
- Saml::Kit::Certificate.new('SP-Signing-Certificate', use: :signing),
- Saml::Kit::Certificate.new('IDP-Signing-Certificate', use: :signing),
+ sp_signing_certificate,
+ idp_signing_certificate,
])
end
it do