@@ -43,12 +43,28 @@ class Document < BaseDocument
4343 # <Object />
4444 # </Signature>
4545 def sign_document ( private_key , certificate , signature_method = RubySaml ::XML ::Crypto ::RSA_SHA256 , digest_method = RubySaml ::XML ::Crypto ::SHA256 )
46+ signature_element = build_signature_element ( private_key , certificate , signature_method , digest_method )
47+ signature_element = convert_nokogiri_to_rexml ( signature_element )
48+ issuer_element = elements [ '//saml:Issuer' ]
49+ if issuer_element
50+ root . insert_after ( issuer_element , signature_element )
51+ elsif ( first_child = root . children [ 0 ] )
52+ root . insert_before ( first_child , signature_element )
53+ else
54+ root . add_element ( signature_element )
55+ end
56+ end
57+
58+ private
59+
60+ def build_signature_element ( private_key , certificate , signature_method , digest_method )
61+ # Parse the original document
4662 noko = Nokogiri ::XML ( to_s ) do |config |
4763 config . options = RubySaml ::XML ::BaseDocument ::NOKOGIRI_OPTIONS
4864 end
4965
50- # Create the signature structure using Builder
51- builder = Nokogiri ::XML ::Builder . new do |xml |
66+ # Build the Signature element
67+ signature_element = Nokogiri ::XML ::Builder . new do |xml |
5268 xml [ 'ds' ] . Signature ( 'xmlns:ds' => RubySaml ::XML ::Crypto ::DSIG ) do
5369 xml [ 'ds' ] . SignedInfo do
5470 xml [ 'ds' ] . CanonicalizationMethod ( Algorithm : RubySaml ::XML ::Crypto ::C14N )
@@ -64,68 +80,46 @@ def sign_document(private_key, certificate, signature_method = RubySaml::XML::Cr
6480 end
6581 end
6682 xml [ 'ds' ] . DigestMethod ( Algorithm : digest_method )
67-
68- # We'll compute and add DigestValue after creating the structure
69- xml [ 'ds' ] . DigestValue
83+ xml [ 'ds' ] . DigestValue ( digest_value ( noko , digest_method ) )
7084 end
7185 end
72-
73- # We'll add these after the digest computation
74- xml [ 'ds' ] . SignatureValue
86+ xml [ 'ds' ] . SignatureValue # Value is added below
7587 xml [ 'ds' ] . KeyInfo do
7688 xml [ 'ds' ] . X509Data do
77- xml [ 'ds' ] . X509Certificate
89+ xml [ 'ds' ] . X509Certificate ( certificate_value ( certificate ) )
7890 end
7991 end
8092 end
81- end
82-
83- # Extract the signature element from the builder
84- signature_element = builder . doc . root
85-
86- # Calculate digest
87- inclusive_namespaces = INC_PREFIX_LIST . split
88- 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 ) )
90-
91- digest_value_element = signature_element . at_xpath ( '//ds:DigestValue' , 'ds' => RubySaml ::XML ::Crypto ::DSIG )
92- digest_value_element . content = digest_value
93-
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 ) )
97- signature = compute_signature ( private_key , RubySaml ::XML ::Crypto . hash_algorithm ( signature_method ) . new , canon_string )
93+ end . doc . root
9894
9995 # Set the signature value
96+ signed_info_element = signature_element . at_xpath ( '//ds:SignedInfo' , 'ds' => RubySaml ::XML ::Crypto ::DSIG )
10097 sig_value_element = signature_element . at_xpath ( '//ds:SignatureValue' , 'ds' => RubySaml ::XML ::Crypto ::DSIG )
101- sig_value_element . content = signature
98+ sig_value_element . content = signature_value ( signed_info_element , private_key , signature_method )
10299
103- # Set the certificate
104- 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 )
106- x509_cert_element . content = Base64 . encode64 ( certificate . to_der ) . delete ( "\n " )
100+ signature_element
101+ end
107102
108- # add the signature
109- signature_element = convert_nokogiri_to_rexml ( signature_element )
110- issuer_element = elements [ '//saml:Issuer' ]
111- if issuer_element
112- root . insert_after ( issuer_element , signature_element )
113- elsif ( first_child = root . children [ 0 ] )
114- root . insert_before ( first_child , signature_element )
115- else
116- root . add_element ( signature_element )
117- end
103+ def digest_value ( document , digest_method )
104+ inclusive_namespaces = INC_PREFIX_LIST . split
105+ canon_algorithm = RubySaml ::XML ::Crypto . canon_algorithm ( RubySaml ::XML ::Crypto ::C14N )
106+ hash_algorithm = RubySaml ::XML ::Crypto . hash_algorithm ( digest_method )
107+
108+ canon_doc = document . canonicalize ( canon_algorithm , inclusive_namespaces )
109+ Base64 . encode64 ( hash_algorithm . digest ( canon_doc ) ) . strip
118110 end
119111
120- private
112+ def signature_value ( signed_info_element , private_key , signature_method )
113+ canon_algorithm = RubySaml ::XML ::Crypto . canon_algorithm ( RubySaml ::XML ::Crypto ::C14N )
114+ hash_algorithm = RubySaml ::XML ::Crypto . hash_algorithm ( signature_method ) . new
121115
122- def compute_signature ( private_key , signature_hash_algorithm , document )
123- Base64 . encode64 ( private_key . sign ( signature_hash_algorithm , document ) ) . delete ( "\n " )
116+ canon_string = signed_info_element . canonicalize ( canon_algorithm )
117+ Base64 . encode64 ( private_key . sign ( hash_algorithm , canon_string ) ) . delete ( "\n " )
124118 end
125119
126- def compute_digest ( document , digest_algorithm )
127- digest = digest_algorithm . digest ( document )
128- Base64 . encode64 ( digest ) . strip
120+ def certificate_value ( certificate )
121+ certificate = OpenSSL :: X509 :: Certificate . new ( certificate ) if certificate . is_a? ( String )
122+ Base64 . encode64 ( certificate . to_der ) . delete ( " \n " )
129123 end
130124
131125 # TODO: This is a shim method which will be removed when the
0 commit comments