main
1# frozen_string_literal: true
2
3RSpec.describe ::Xml::Kit::Signatures do
4 let(:reference_id) { Xml::Kit::Id.generate }
5 let(:options) { { 'xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol', 'xmlns:saml' => 'urn:oasis:names:tc:SAML:2.0:assertion', ID: reference_id } }
6 let(:key_pair) { ::Xml::Kit::KeyPair.generate(use: :signing) }
7
8 context 'when a key pair is specified' do
9 let(:signed_xml) do
10 described_class.sign(key_pair: key_pair) do |xml, signature|
11 xml.tag!('samlp:AuthnRequest', options) do
12 signature.template(reference_id)
13 xml.tag!('saml:Issuer', 'MyEntityID')
14 end
15 end
16 end
17 let(:result) { Hash.from_xml(signed_xml) }
18 let(:signature) { result['AuthnRequest']['Signature'] }
19 let(:expected_certificate) { key_pair.certificate.stripped }
20
21 specify { expect(signature['xmlns']).to eql('http://www.w3.org/2000/09/xmldsig#') }
22 specify { expect(signature['SignedInfo']['CanonicalizationMethod']['Algorithm']).to eql('http://www.w3.org/2001/10/xml-exc-c14n#') }
23 specify { expect(signature['SignedInfo']['SignatureMethod']['Algorithm']).to eql('http://www.w3.org/2001/04/xmldsig-more#rsa-sha256') }
24 specify { expect(signature['SignedInfo']['Reference']['URI']).to eql("##{reference_id}") }
25 specify { expect(signature['SignedInfo']['Reference']['DigestMethod']['Algorithm']).to eql('http://www.w3.org/2001/04/xmlenc#sha256') }
26 specify { expect(signature['KeyInfo']['X509Data']['X509Certificate']).to eql(expected_certificate) }
27 specify { expect(signature['SignedInfo']['Reference']['DigestValue']).to be_present }
28 specify { expect(signature['SignatureValue']).to be_present }
29 specify { expect(OpenSSL::X509::Certificate.new(Base64.decode64(signature['KeyInfo']['X509Data']['X509Certificate']))).to be_present }
30
31 specify do
32 expect(signature['SignedInfo']['Reference']['Transforms']['Transform']).to match_array([
33 { 'Algorithm' => 'http://www.w3.org/2000/09/xmldsig#enveloped-signature' },
34 { 'Algorithm' => 'http://www.w3.org/2001/10/xml-exc-c14n#' }
35 ])
36 end
37 end
38
39 context 'when a key pair is not specified' do
40 let(:signed_xml) do
41 described_class.sign(key_pair: nil) do |xml, signature|
42 xml.AuthnRequest do
43 signature.template(reference_id)
44 xml.Issuer 'MyEntityID'
45 end
46 end
47 end
48 let(:result) { Hash.from_xml(signed_xml) }
49
50 specify { expect(result['AuthnRequest']).to be_present }
51 specify { expect(result['AuthnRequest']['Signature']).to be_nil }
52 end
53
54 context 'when the signature is embedded' do
55 let(:result) do
56 described_class.sign(key_pair: key_pair) do |xml, signature|
57 xml.tag!('saml:Assertion', options) do
58 signature.template(reference_id)
59 xml.tag! 'saml:Subject' do
60 xml.NameID Format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'
61 end
62 end
63 end
64 end
65
66 it 'produces a valid signature' do
67 node = Nokogiri::XML(result).at_xpath('//ds:Signature', ds: ::Xml::Kit::Namespaces::XMLDSIG)
68 dsignature = Xmldsig::Signature.new(node, 'ID=$uri or @Id')
69 expect(dsignature).to be_valid(key_pair.certificate.x509)
70 expect(dsignature.errors).to be_empty
71 end
72 end
73end