Skip to content

Commit e96becf

Browse files
committed
Related to #207 Add ability to sign metadata (Test improved, prettyprint optional on metadata generator, signature position fixed). Fix algorithm calculator
1 parent 0a9649c commit e96becf

File tree

4 files changed

+36
-13
lines changed

4 files changed

+36
-13
lines changed

lib/onelogin/ruby-saml/metadata.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require "uri"
2+
require "uuid"
23

34
require "onelogin/ruby-saml/logging"
45

@@ -9,7 +10,7 @@
910
module OneLogin
1011
module RubySaml
1112
class Metadata
12-
def generate(settings)
13+
def generate(settings, pretty_print=true)
1314
meta_doc = XMLSecurity::Document.new
1415
root = meta_doc.add_element "md:EntityDescriptor", {
1516
"xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
@@ -20,6 +21,7 @@ def generate(settings)
2021
# However we would like assertions signed if idp_cert_fingerprint or idp_cert is set
2122
"WantAssertionsSigned" => !!(settings.idp_cert_fingerprint || settings.idp_cert)
2223
}
24+
root.attributes["ID"] = "_" + UUID.new.generate
2325
if settings.issuer
2426
root.attributes["entityID"] = settings.issuer
2527
end
@@ -91,7 +93,11 @@ def generate(settings)
9193

9294
ret = ""
9395
# pretty print the XML so IdP administrators can easily see what the SP supports
94-
meta_doc.write(ret, 1)
96+
if pretty_print
97+
meta_doc.write(ret, 1)
98+
else
99+
ret = meta_doc.to_s
100+
end
95101

96102
return ret
97103
end

lib/onelogin/ruby-saml/settings.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ def get_sp_key
111111
:security => {
112112
:authn_requests_signed => false,
113113
:logout_requests_signed => false,
114-
:logout_responses_signed => false,
114+
:logout_responses_signed => false,
115+
:metadata_signed => false,
115116
:embed_sign => false,
116117
:digest_method => XMLSecurity::Document::SHA1,
117118
:signature_method => XMLSecurity::Document::RSA_SHA1

lib/xml_security.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ def algorithm(element)
5656
algorithm = element
5757
if algorithm.is_a?(REXML::Element)
5858
algorithm = element.attribute("Algorithm").value
59-
algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
6059
end
6160

61+
algorithm = algorithm && algorithm =~ /(rsa-)?sha(.*?)$/i && $2.to_i
62+
6263
case algorithm
6364
when 256 then OpenSSL::Digest::SHA256
6465
when 384 then OpenSSL::Digest::SHA384
@@ -80,7 +81,7 @@ class Document < BaseDocument
8081
SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"
8182
SHA512 = "http://www.w3.org/2001/04/xmldsig-more#sha512"
8283
ENVELOPED_SIG = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
83-
INC_PREFIX_LIST = "#default samlp saml ds xs xsi"
84+
INC_PREFIX_LIST = "#default samlp saml ds xs xsi md"
8485

8586
attr_accessor :uuid
8687

@@ -119,8 +120,6 @@ def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_
119120
# Add Transforms
120121
transforms_element = reference_element.add_element("ds:Transforms")
121122
transforms_element.add_element("ds:Transform", {"Algorithm" => ENVELOPED_SIG})
122-
#transforms_element.add_element("ds:Transform", {"Algorithm" => C14N})
123-
#transforms_element.add_element("ds:InclusiveNamespaces", {"xmlns" => C14N, "PrefixList" => INC_PREFIX_LIST})
124123
c14element = transforms_element.add_element("ds:Transform", {"Algorithm" => C14N})
125124
c14element.add_element("ec:InclusiveNamespaces", {"xmlns:ec" => C14N, "PrefixList" => INC_PREFIX_LIST})
126125

@@ -133,6 +132,7 @@ def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_
133132
noko_sig_element = Nokogiri.parse(signature_element.to_s)
134133
noko_signed_info_element = noko_sig_element.at_xpath('//ds:Signature/ds:SignedInfo', 'ds' => DSIG)
135134
canon_string = noko_signed_info_element.canonicalize(canon_algorithm(C14N))
135+
136136
signature = compute_signature(private_key, algorithm(signature_method).new, canon_string)
137137
signature_element.add_element("ds:SignatureValue").text = signature
138138

@@ -150,7 +150,11 @@ def sign_document(private_key, certificate, signature_method = RSA_SHA1, digest_
150150
if issuer_element
151151
self.root.insert_after issuer_element, signature_element
152152
else
153-
self.root.add_element(signature_element)
153+
if sp_sso_descriptor = self.elements["/md:EntityDescriptor"]
154+
self.root.insert_before sp_sso_descriptor, signature_element
155+
else
156+
self.root.add_element(signature_element)
157+
end
154158
end
155159
end
156160

test/metadata_test.rb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class MetadataTest < Minitest::Test
66

77
describe 'Metadata' do
88
let(:settings) { OneLogin::RubySaml::Settings.new }
9-
let(:xml_text) { OneLogin::RubySaml::Metadata.new.generate(settings) }
9+
let(:xml_text) { OneLogin::RubySaml::Metadata.new.generate(settings, false) }
1010
let(:xml_doc) { REXML::Document.new(xml_text) }
1111
let(:spsso_descriptor) { REXML::XPath.first(xml_doc, "//md:SPSSODescriptor") }
1212
let(:acs) { REXML::XPath.first(xml_doc, "//md:AssertionConsumerService") }
@@ -17,9 +17,15 @@ class MetadataTest < Minitest::Test
1717
settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
1818
end
1919

20+
it "generates Pretty Print Service Provider Metadata" do
21+
start = "<?xml version='1.0' encoding='UTF-8'?>\n<md:EntityDescriptor"
22+
xml_text_2 = OneLogin::RubySaml::Metadata.new.generate(settings, true)
23+
assert xml_text_2[0..start.length-1] == start
24+
end
25+
2026
it "generates Service Provider Metadata" do
2127
# assert correct xml declaration
22-
start = "<?xml version='1.0' encoding='UTF-8'?>\n<md:EntityDescriptor"
28+
start = "<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor"
2329
assert xml_text[0..start.length-1] == start
2430

2531
assert_equal "https://example.com", REXML::XPath.first(xml_doc, "//md:EntityDescriptor").attribute("entityID").value
@@ -89,19 +95,25 @@ class MetadataTest < Minitest::Test
8995
it "creates a signed metadata" do
9096
assert_match %r[<ds:SignatureValue>\s*([a-zA-Z0-9/+=]+)\s*</ds:SignatureValue>]m, xml_text
9197
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], xml_text
92-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], xml_text
98+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], xml_text
99+
signed_metadata = XMLSecurity::SignedDocument.new(xml_text)
100+
assert signed_metadata.validate_document(ruby_saml_cert_fingerprint, false)
93101
end
94102

95103
describe "when digest and signature methods are specified" do
96104
before do
97-
settings.security[:signature_method] = XMLSecurity::Document::SHA256
105+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
98106
settings.security[:digest_method] = XMLSecurity::Document::SHA512
99107
end
100108

101109
it "creates a signed metadata with specified digest and signature methods" do
102110
assert_match %r[<ds:SignatureValue>\s*([a-zA-Z0-9/+=]+)\s*</ds:SignatureValue>]m, xml_text
103111
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], xml_text
104-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'/>], xml_text
112+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#sha512'/>], xml_text
113+
114+
signed_metadata_2 = XMLSecurity::SignedDocument.new(xml_text)
115+
116+
assert signed_metadata_2.validate_document(ruby_saml_cert_fingerprint, false)
105117
end
106118
end
107119
end

0 commit comments

Comments
 (0)