Skip to content

Commit 73c8f25

Browse files
committed
Cleanup; use Nokogiri builder
1 parent f15e5fd commit 73c8f25

File tree

1 file changed

+54
-66
lines changed

1 file changed

+54
-66
lines changed

lib/ruby_saml/xml/document.rb

Lines changed: 54 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -47,79 +47,62 @@ def sign_document(private_key, certificate, signature_method = RubySaml::XML::Cr
4747
config.options = RubySaml::XML::BaseDocument::NOKOGIRI_OPTIONS
4848
end
4949

50-
# Create signature elements using Nokogiri
51-
signature_element = Nokogiri::XML::Element.new('ds:Signature', noko)
52-
signature_element['xmlns:ds'] = RubySaml::XML::Crypto::DSIG
53-
54-
signed_info_element = Nokogiri::XML::Element.new('ds:SignedInfo', noko)
55-
signature_element.add_child(signed_info_element)
56-
57-
canon_method_element = Nokogiri::XML::Element.new('ds:CanonicalizationMethod', noko)
58-
canon_method_element['Algorithm'] = RubySaml::XML::Crypto::C14N
59-
signed_info_element.add_child(canon_method_element)
60-
61-
sig_method_element = Nokogiri::XML::Element.new('ds:SignatureMethod', noko)
62-
sig_method_element['Algorithm'] = signature_method
63-
signed_info_element.add_child(sig_method_element)
64-
65-
# Add Reference
66-
reference_element = Nokogiri::XML::Element.new('ds:Reference', noko)
67-
reference_element['URI'] = "##{noko.root&.attr('ID')}"
68-
signed_info_element.add_child(reference_element)
69-
70-
# Add Transforms
71-
transforms_element = Nokogiri::XML::Element.new('ds:Transforms', noko)
72-
reference_element.add_child(transforms_element)
73-
74-
transform1 = Nokogiri::XML::Element.new('ds:Transform', noko)
75-
transform1['Algorithm'] = RubySaml::XML::Crypto::ENVELOPED_SIG
76-
transforms_element.add_child(transform1)
77-
78-
transform2 = Nokogiri::XML::Element.new('ds:Transform', noko)
79-
transform2['Algorithm'] = RubySaml::XML::Crypto::C14N
80-
transforms_element.add_child(transform2)
81-
82-
inc_namespaces = Nokogiri::XML::Element.new('ec:InclusiveNamespaces', noko)
83-
inc_namespaces['xmlns:ec'] = RubySaml::XML::Crypto::C14N
84-
inc_namespaces['PrefixList'] = INC_PREFIX_LIST
85-
transform2.add_child(inc_namespaces)
50+
# Create the signature structure using Builder
51+
builder = Nokogiri::XML::Builder.new do |xml|
52+
xml['ds'].Signature('xmlns:ds' => RubySaml::XML::Crypto::DSIG) do
53+
xml['ds'].SignedInfo do
54+
xml['ds'].CanonicalizationMethod(Algorithm: RubySaml::XML::Crypto::C14N)
55+
xml['ds'].SignatureMethod(Algorithm: signature_method)
56+
xml['ds'].Reference(URI: "##{noko.root.attr('ID')}") do
57+
xml['ds'].Transforms do
58+
xml['ds'].Transform(Algorithm: RubySaml::XML::Crypto::ENVELOPED_SIG)
59+
xml['ds'].Transform(Algorithm: RubySaml::XML::Crypto::C14N) do
60+
xml['ec'].InclusiveNamespaces(
61+
'xmlns:ec' => RubySaml::XML::Crypto::C14N,
62+
PrefixList: INC_PREFIX_LIST
63+
)
64+
end
65+
end
66+
xml['ds'].DigestMethod(Algorithm: digest_method)
67+
68+
# We'll compute and add DigestValue after creating the structure
69+
xml['ds'].DigestValue
70+
end
71+
end
72+
73+
# We'll add these after the digest computation
74+
xml['ds'].SignatureValue
75+
xml['ds'].KeyInfo do
76+
xml['ds'].X509Data do
77+
xml['ds'].X509Certificate
78+
end
79+
end
80+
end
81+
end
8682

87-
digest_method_element = Nokogiri::XML::Element.new('ds:DigestMethod', noko)
88-
digest_method_element['Algorithm'] = digest_method
89-
reference_element.add_child(digest_method_element)
83+
# Extract the signature element from the builder
84+
signature_element = builder.doc.root
9085

86+
# Calculate digest
9187
inclusive_namespaces = INC_PREFIX_LIST.split
9288
canon_doc = noko.canonicalize(RubySaml::XML::Crypto.canon_algorithm(RubySaml::XML::Crypto::C14N), inclusive_namespaces)
89+
digest_value = compute_digest(canon_doc, RubySaml::XML::Crypto.hash_algorithm(digest_method))
9390

94-
digest_value_element = Nokogiri::XML::Element.new('ds:DigestValue', noko)
95-
digest_value_element.content = compute_digest(canon_doc, RubySaml::XML::Crypto.hash_algorithm(digest_method_element))
96-
reference_element.add_child(digest_value_element)
97-
98-
# add SignatureValue
99-
noko_sig_element = Nokogiri::XML(signature_element.to_s) do |config|
100-
config.options = RubySaml::XML::BaseDocument::NOKOGIRI_OPTIONS | Nokogiri::XML::ParseOptions::NOBLANKS
101-
end
102-
103-
noko_signed_info_element = noko_sig_element.at_xpath('//ds:Signature/ds:SignedInfo', 'ds' => RubySaml::XML::Crypto::DSIG)
104-
canon_string = noko_signed_info_element.canonicalize(RubySaml::XML::Crypto.canon_algorithm(RubySaml::XML::Crypto::C14N))
91+
digest_value_element = signature_element.at_xpath('//ds:DigestValue', 'ds' => RubySaml::XML::Crypto::DSIG)
92+
digest_value_element.content = digest_value
10593

94+
# Canonicalize the SignedInfo element for signing
95+
signed_info_element = signature_element.at_xpath('//ds:SignedInfo', 'ds' => RubySaml::XML::Crypto::DSIG)
96+
canon_string = signed_info_element.canonicalize(RubySaml::XML::Crypto.canon_algorithm(RubySaml::XML::Crypto::C14N))
10697
signature = compute_signature(private_key, RubySaml::XML::Crypto.hash_algorithm(signature_method).new, canon_string)
10798

108-
sig_value_element = Nokogiri::XML::Element.new('ds:SignatureValue', noko)
99+
# Set the signature value
100+
sig_value_element = signature_element.at_xpath('//ds:SignatureValue', 'ds' => RubySaml::XML::Crypto::DSIG)
109101
sig_value_element.content = signature
110-
signature_element.add_child(sig_value_element)
111-
112-
# add KeyInfo
113-
key_info_element = Nokogiri::XML::Element.new('ds:KeyInfo', noko)
114-
signature_element.add_child(key_info_element)
115-
116-
x509_element = Nokogiri::XML::Element.new('ds:X509Data', noko)
117-
key_info_element.add_child(x509_element)
118-
119-
x509_cert_element = Nokogiri::XML::Element.new('ds:X509Certificate', noko)
120-
x509_element.add_child(x509_cert_element)
121102

103+
# Set the certificate
122104
certificate = OpenSSL::X509::Certificate.new(certificate) if certificate.is_a?(String)
105+
x509_cert_element = signature_element.at_xpath('//ds:X509Certificate', 'ds' => RubySaml::XML::Crypto::DSIG)
123106
x509_cert_element.content = Base64.encode64(certificate.to_der).delete("\n")
124107

125108
# add the signature
@@ -145,10 +128,15 @@ def compute_digest(document, digest_algorithm)
145128
Base64.encode64(digest).strip
146129
end
147130

148-
def convert_nokogiri_to_rexml(noko_element)
149-
rexml_element = REXML::Element.new(noko_element.name)
131+
# TODO: This is a shim method which will be removed when the
132+
# full Nokogiri conversion is complete
133+
def convert_nokogiri_to_rexml(noko_element, parent_namespaces = Set.new)
134+
rexml_element = REXML::Element.new("#{"#{noko_element.namespace.prefix}:" if noko_element.namespace}#{noko_element.name}")
135+
136+
if noko_element.namespace && !parent_namespaces.include?(noko_element.namespace)
137+
rexml_element.add_namespace(noko_element.namespace.prefix, noko_element.namespace.href)
138+
end
150139

151-
# Copy attributes
152140
noko_element.attributes.each do |name, value|
153141
rexml_element.add_attribute(name, value)
154142
end
@@ -161,7 +149,7 @@ def convert_nokogiri_to_rexml(noko_element)
161149
# Recursively copy child elements
162150
noko_element.children.each do |child|
163151
if child.element?
164-
rexml_element.add_element(convert_nokogiri_to_rexml(child))
152+
rexml_element.add_element(convert_nokogiri_to_rexml(child, parent_namespaces << noko_element.namespace))
165153
elsif child.text?
166154
rexml_element.add_text(child.text)
167155
end

0 commit comments

Comments
 (0)