Class: Signer
- Inherits:
-
Object
- Object
- Signer
- Defined in:
- lib/signer.rb,
lib/signer/version.rb,
lib/signer/digester.rb
Defined Under Namespace
Classes: Digester
Constant Summary collapse
- WSU_NAMESPACE =
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'.freeze
- WSSE_NAMESPACE =
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'.freeze
- DS_NAMESPACE =
'http://www.w3.org/2000/09/xmldsig#'.freeze
- SIGNATURE_ALGORITHM =
{ # SHA 1 sha1: { id: 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', name: 'SHA1' }, # SHA 256 sha256: { id: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', name: 'SHA256' }, # SHA512 sha512: { id: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512', name: 'SHA512' }, # GOST R 34-11 94 gostr3411: { id: 'http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411', name: 'GOST R 34.11-94' }, # GOST R 34-11 2012 256 bit gostr34112012_256: { id: 'urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256', name: 'GOST R 34.11-2012 256', }, }.freeze
- CANONICALIZE_ALGORITHM =
{ c14n_exec_1_0: { name: 'c14n execlusive 1.0', value: Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0, id: 'http://www.w3.org/2001/10/xml-exc-c14n#' }, c14n_1_0: { name: 'c14n 1.0', value: Nokogiri::XML::XML_C14N_1_0, id: 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' }, c14n_1_1: { name: 'c14n 1.1', value: Nokogiri::XML::XML_C14N_1_1, id: 'https://www.w3.org/TR/2008/REC-xml-c14n11-20080502/' } }.freeze
- VERSION =
'1.10.0'- DIGEST_ALGORITHMS =
Digest algorithms supported "out of the box"
{ # SHA 1 sha1: { name: 'SHA1', id: 'http://www.w3.org/2000/09/xmldsig#sha1', digester: lambda { OpenSSL::Digest::SHA1.new }, }, # SHA 256 sha256: { name: 'SHA256', id: 'http://www.w3.org/2001/04/xmlenc#sha256', digester: lambda { OpenSSL::Digest::SHA256.new }, }, # SHA512 sha512: { name: 'SHA512', id: 'http://www.w3.org/2001/04/xmlenc#sha512', digester: lambda { OpenSSL::Digest::SHA512.new }, }, # GOST R 34-11 94 gostr3411: { name: 'GOST R 34.11-94', id: 'http://www.w3.org/2001/04/xmldsig-more#gostr3411', digester: lambda { OpenSSL::Digest.new('md_gost94') }, }, # GOST R 34-11 2012 256 bit gostr34112012_256: { name: 'GOST R 34.11-2012 256', id: 'urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256', digester: lambda { begin OpenSSL::Digest.new('streebog256') rescue OpenSSL::Digest.new('md_gost12_256') end }, }, }.freeze
Instance Attribute Summary collapse
-
#cert ⇒ Object
Returns the value of attribute cert.
-
#document ⇒ Object
Returns the value of attribute document.
-
#ds_namespace_prefix ⇒ Object
Returns the value of attribute ds_namespace_prefix.
-
#private_key ⇒ Object
Returns the value of attribute private_key.
- #security_node ⇒ Object
- #security_token_id ⇒ Object
-
#signature_algorithm_id ⇒ Object
Returns the value of attribute signature_algorithm_id.
-
#signature_node ⇒ Object
. -
#wss ⇒ Object
Returns the value of attribute wss.
Instance Method Summary collapse
-
#binary_security_token_node ⇒ Object
<o:BinarySecurityToken u:Id="" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"> ...
- #canonicalize(node = document, inclusive_namespaces = nil, algorithm: canonicalize_algorithm) ⇒ Object
- #canonicalize_algorithm ⇒ Object
- #canonicalize_algorithm=(algorithm) ⇒ Object
- #canonicalize_id ⇒ Object
- #canonicalize_name ⇒ Object
-
#digest!(target_node, options = {}) ⇒ Object
Digests some
target_node, which integrity you wish to track. -
#digest_algorithm ⇒ Object
Return symbol name for supported digest algorithms and string name for custom ones.
-
#digest_algorithm=(algorithm) ⇒ Object
Allows to change algorithm for node digesting (default is SHA1).
-
#initialize(document, noblanks: true, wss: true, canonicalize_algorithm: :c14n_exec_1_0) ⇒ Signer
constructor
A new instance of Signer.
-
#sign!(options = {}) ⇒ Object
Sign document with provided certificate, private key and other options.
-
#signature_digest_algorithm ⇒ Object
Return symbol name for supported digest algorithms and string name for custom ones.
-
#signature_digest_algorithm=(algorithm) ⇒ Object
Allows to change digesting algorithm for signature creation.
-
#signed_info_node ⇒ Object
... - #to_xml ⇒ Object
-
#x509_data_node(issuer_in_security_token = false) ⇒ Object
.(optional) (optional)System.Security.Cryptography.X509Certificates.X500DistinguishedName 13070789 MIID+jCCAuKgAwIBAgIEAMdxxTANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQGEwJTRTEeMBwGA1UEChMVTm9yZGVhIEJhbmsgQUIgKHB1YmwpMScwJQYDVQQDEx5Ob3JkZWEgcm9sZS1jZXJ0aWZpY2F0ZXMgQ0EgMDExFDASBgNVBAUTCzUxNjQwNi0wMTIwMB4XDTA5MDYxMTEyNTAxOVoXDTExMDYxMTEyNTAxOVowcjELMAkGA1UEBhMCU0UxIDAeBgNVBAMMF05vcmRlYSBEZW1vIENlcnRpZmljYXRlMRQwEgYDVQQEDAtDZXJ0aWZpY2F0ZTEUMBIGA1UEKgwLTm9yZGVhIERlbW8xFTATBgNVBAUTDDAwOTU1NzI0Mzc3MjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwcgz5AzbxTbsCE51No7fPnSqmQBIMW9OiPkiHotwYQTl+H9qwDvQRyBqHN26tnw7hNvEShd1ZRGUg4drMEXDV5CmKqsAevs9lauWDaHnGKPNHZJ1hNNYXHwymksEz5zMnG8eqRdhb4vOV2FzreJeYpsgx31Bv0aTofHcHVz4uGcCAwEAAaOCASAwggEcMAkGA1UdEwQCMAAwEQYDVR0OBAoECEj6Y9/vU03WMBMGA1UdIAQMMAowCAYGKoVwRwEDMBMGA1UdIwQMMAqACEIFjfLBeTpRMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3Aubm9yZGVhLnNlL1JDQTAxMA4GA1UdDwEB/wQEAwIGQDCBiAYDVR0fBIGAMH4wfKB6oHiGdmxkYXA6Ly9sZGFwLm5iLnNlL2NuPU5vcmRlYSUyMHJvbGUtY2VydGlmaWNhdGVzJTIwQ0ElMjAwMSxvPU5vcmRlYSUyMEJhbmslMjBBQiUyMChwdWJsKSxjPVNFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwDQYJKoZIhvcNAQEFBQADggEBAEXUv87VpHk51y3TqkMb1MYDqeKvQRE1cNcvhEJhIzdDpXMA9fG0KqvSTT1e0ZI2r78mXDvtTZnpic44jX2XMSmKO6n+1taAXq940tJUhF4arYMUxwDKOso0Doanogug496gipqMlpLgvIhGt06sWjNrvHzp2eGydUFdCsLr2ULqbDcut7g6eMcmrsnrOntjEU/J3hO8gyCeldJ+fI81qarrK/I0MZLR5LWCyVG/SKduoxHLX7JohsbIGyK1qAh9fi8l6X1Rcu80v5inpu71E/DnjbkAZBo7vsj78zzdk7KNliBIqBcIszdJ3dEHRWSI7FspRxyiR0NDm4lpyLwFtfw=
Constructor Details
#initialize(document, noblanks: true, wss: true, canonicalize_algorithm: :c14n_exec_1_0) ⇒ Signer
Returns a new instance of Signer.
64 65 66 67 68 69 70 71 72 73 |
# File 'lib/signer.rb', line 64 def initialize(document, noblanks: true, wss: true, canonicalize_algorithm: :c14n_exec_1_0) self.document = Nokogiri::XML(document.to_s) do |config| config.noblanks if noblanks end self.document.namespace_inheritance = true if self.document.respond_to?(:namespace_inheritance) self.digest_algorithm = :sha1 self.wss = wss self.canonicalize_algorithm = canonicalize_algorithm self.signature_digest_algorithm = :sha1 end |
Instance Attribute Details
#cert ⇒ Object
Returns the value of attribute cert.
11 12 13 |
# File 'lib/signer.rb', line 11 def cert @cert end |
#document ⇒ Object
Returns the value of attribute document.
10 11 12 |
# File 'lib/signer.rb', line 10 def document @document end |
#ds_namespace_prefix ⇒ Object
Returns the value of attribute ds_namespace_prefix.
10 11 12 |
# File 'lib/signer.rb', line 10 def ds_namespace_prefix @ds_namespace_prefix end |
#private_key ⇒ Object
Returns the value of attribute private_key.
10 11 12 |
# File 'lib/signer.rb', line 10 def private_key @private_key end |
#security_node ⇒ Object
136 137 138 |
# File 'lib/signer.rb', line 136 def security_node @security_node ||= wss? ? document.xpath('//wsse:Security', wsse: WSSE_NAMESPACE).first : '' end |
#security_token_id ⇒ Object
132 133 134 |
# File 'lib/signer.rb', line 132 def security_token_id @security_token_id ||= wss? ? "uuid-639b8970-7644-4f9e-9bc4-9c2e367808fc-1" : "" end |
#signature_algorithm_id ⇒ Object
Returns the value of attribute signature_algorithm_id.
10 11 12 |
# File 'lib/signer.rb', line 10 def signature_algorithm_id @signature_algorithm_id end |
#signature_node ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/signer.rb', line 145 def signature_node @signature_node ||= begin @signature_node = security_node.at_xpath('ds:Signature', ds: DS_NAMESPACE) unless @signature_node @signature_node = Nokogiri::XML::Node.new('Signature', document) set_namespace_for_node(@signature_node, DS_NAMESPACE, ds_namespace_prefix) security_node.add_child(@signature_node) end @signature_node end end |
#wss ⇒ Object
Returns the value of attribute wss.
10 11 12 |
# File 'lib/signer.rb', line 10 def wss @wss end |
Instance Method Details
#binary_security_token_node ⇒ Object
<o:BinarySecurityToken u:Id="" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
...
</o:BinarySecurityToken>
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/signer.rb', line 191 def binary_security_token_node return unless wss? node = document.at_xpath('wsse:BinarySecurityToken', wsse: WSSE_NAMESPACE) unless node node = Nokogiri::XML::Node.new('BinarySecurityToken', document) node['ValueType'] = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3' node['EncodingType'] = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary' node.content = Base64.encode64(cert.to_der).gsub("\n", '') signature_node.add_previous_sibling(node) wsse_ns = namespace_prefix(node, WSSE_NAMESPACE, 'wsse') wsu_ns = namespace_prefix(node, WSU_NAMESPACE, 'wsu') node["#{wsu_ns}:Id"] = security_token_id key_info_node = Nokogiri::XML::Node.new('KeyInfo', document) security_token_reference_node = Nokogiri::XML::Node.new("#{wsse_ns}:SecurityTokenReference", document) key_info_node.add_child(security_token_reference_node) set_namespace_for_node(key_info_node, DS_NAMESPACE, ds_namespace_prefix) reference_node = Nokogiri::XML::Node.new("#{wsse_ns}:Reference", document) reference_node['ValueType'] = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3' reference_node['URI'] = "##{security_token_id}" security_token_reference_node.add_child(reference_node) signed_info_node.add_next_sibling(key_info_node) end node end |
#canonicalize(node = document, inclusive_namespaces = nil, algorithm: canonicalize_algorithm) ⇒ Object
140 141 142 |
# File 'lib/signer.rb', line 140 def canonicalize(node = document, inclusive_namespaces=nil, algorithm: canonicalize_algorithm) node.canonicalize(algorithm, inclusive_namespaces, nil) end |
#canonicalize_algorithm ⇒ Object
87 88 89 |
# File 'lib/signer.rb', line 87 def canonicalize_algorithm @canonicalize_algorithm[:value] end |
#canonicalize_algorithm=(algorithm) ⇒ Object
91 92 93 |
# File 'lib/signer.rb', line 91 def canonicalize_algorithm=(algorithm) @canonicalize_algorithm = CANONICALIZE_ALGORITHM[algorithm] end |
#canonicalize_id ⇒ Object
83 84 85 |
# File 'lib/signer.rb', line 83 def canonicalize_id @canonicalize_algorithm[:id] end |
#canonicalize_name ⇒ Object
79 80 81 |
# File 'lib/signer.rb', line 79 def canonicalize_name @canonicalize_algorithm[:name] end |
#digest!(target_node, options = {}) ⇒ Object
Digests some target_node, which integrity you wish to track. Any changes in digested node will invalidate signed message.
All digest should be calculated before signing.
Available options:
- [+:id+] Id for the node, if you don't want to use automatically calculated one
- [+:inclusive_namespaces+] Array of namespace prefixes which definitions should be added to node during canonicalization
- [+:enveloped+]
- [+:enveloped_legacy+] add solely
enveloped-signatureinTransformswith :enveloped:. - [+:ref_type+] add
Typeattribute to Reference node, if ref_type is not nil
Example of XML that will be inserted in message for call like digest!(node, inclusive_namespaces: ['soap']):
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/signer.rb', line 289 def digest!(target_node, = {}) if wss? wsu_ns = namespace_prefix(target_node, WSU_NAMESPACE) current_id = target_node["#{wsu_ns}:Id"] if wsu_ns id = [:id] || current_id || "_#{Digest::SHA1.hexdigest(target_node.to_s)}" unless id.to_s.empty? wsu_ns ||= namespace_prefix(target_node, WSU_NAMESPACE, 'wsu') target_node["#{wsu_ns}:Id"] = id.to_s end elsif target_node['Id'].nil? id = [:id] || "_#{Digest::SHA1.hexdigest(target_node.to_s)}" target_node['Id'] = id.to_s unless id.empty? else id = [:id] || target_node['Id'] end target_canon = canonicalize(target_node, [:inclusive_namespaces]) target_digest = Base64.encode64(@digester.digest(target_canon)).strip reference_node = Nokogiri::XML::Node.new('Reference', document) reference_node['URI'] = id.to_s.size > 0 ? "##{id}" : "" reference_node['Type'] = [:ref_type] if [:ref_type] signed_info_node.add_child(reference_node) set_namespace_for_node(reference_node, DS_NAMESPACE, ds_namespace_prefix) transforms_node = Nokogiri::XML::Node.new('Transforms', document) reference_node.add_child(transforms_node) unless [:no_transform] set_namespace_for_node(transforms_node, DS_NAMESPACE, ds_namespace_prefix) # create reference + transforms node transform!(transforms_node, ) digest_method_node = Nokogiri::XML::Node.new('DigestMethod', document) digest_method_node['Algorithm'] = @digester.digest_id reference_node.add_child(digest_method_node) set_namespace_for_node(digest_method_node, DS_NAMESPACE, ds_namespace_prefix) digest_value_node = Nokogiri::XML::Node.new('DigestValue', document) digest_value_node.content = target_digest reference_node.add_child(digest_value_node) set_namespace_for_node(digest_value_node, DS_NAMESPACE, ds_namespace_prefix) self end |
#digest_algorithm ⇒ Object
Return symbol name for supported digest algorithms and string name for custom ones.
96 97 98 |
# File 'lib/signer.rb', line 96 def digest_algorithm @digester.symbol || @digester.digest_name end |
#digest_algorithm=(algorithm) ⇒ Object
Allows to change algorithm for node digesting (default is SHA1).
You may pass either a one of :sha1, :sha256 or :gostr3411 symbols
or Hash with keys :id with a string, which will denote algorithm in XML Reference tag
and :digester with instance of class with interface compatible with OpenSSL::Digest class.
105 106 107 |
# File 'lib/signer.rb', line 105 def digest_algorithm=(algorithm) @digester = Signer::Digester.new(algorithm) end |
#sign!(options = {}) ⇒ Object
Sign document with provided certificate, private key and other options
This should be very last action before calling to_xml, all the required nodes should be digested with digest! before signing.
Available options:
- [+:security_token+] Serializes certificate in DER format, encodes it with Base64 and inserts it within
<BinarySecurityToken>tag - [+:issuer_serial+]
- [+:issuer_in_security_token+]
- [+:inclusive_namespaces+] Array of namespace prefixes which definitions should be added to signed info node during canonicalization
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/signer.rb', line 346 def sign!( = {}) if [:security_token] binary_security_token_node end if [:issuer_serial] x509_data_node([:issuer_in_security_token]) end if [:inclusive_namespaces] c14n_method_node = signed_info_node.at_xpath('ds:CanonicalizationMethod', ds: DS_NAMESPACE) inclusive_namespaces_node = Nokogiri::XML::Node.new('ec:InclusiveNamespaces', document) inclusive_namespaces_node.add_namespace_definition('ec', c14n_method_node['Algorithm']) inclusive_namespaces_node['PrefixList'] = [:inclusive_namespaces].join(' ') c14n_method_node.add_child(inclusive_namespaces_node) end signed_info_canon = canonicalize(signed_info_node, [:inclusive_namespaces]) signature = private_key.sign(@sign_digester.digester, signed_info_canon) signature_value_digest = Base64.encode64(signature).delete("\n") signature_value_node = Nokogiri::XML::Node.new('SignatureValue', document) signature_value_node.content = signature_value_digest signed_info_node.add_next_sibling(signature_value_node) set_namespace_for_node(signature_value_node, DS_NAMESPACE, ds_namespace_prefix) self end |
#signature_digest_algorithm ⇒ Object
Return symbol name for supported digest algorithms and string name for custom ones.
110 111 112 |
# File 'lib/signer.rb', line 110 def signature_digest_algorithm @sign_digester.symbol || @sign_digester.digest_name end |
#signature_digest_algorithm=(algorithm) ⇒ Object
Allows to change digesting algorithm for signature creation. Same as digest_algorithm=
115 116 117 118 |
# File 'lib/signer.rb', line 115 def signature_digest_algorithm=(algorithm) @sign_digester = Signer::Digester.new(algorithm) self.signature_algorithm_id = SIGNATURE_ALGORITHM[algorithm][:id] end |
#signed_info_node ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/signer.rb', line 162 def signed_info_node node = signature_node.at_xpath('ds:SignedInfo', ds: DS_NAMESPACE) unless node node = Nokogiri::XML::Node.new('SignedInfo', document) signature_node.add_child(node) set_namespace_for_node(node, DS_NAMESPACE, ds_namespace_prefix) canonicalization_method_node = Nokogiri::XML::Node.new('CanonicalizationMethod', document) canonicalization_method_node['Algorithm'] = canonicalize_id node.add_child(canonicalization_method_node) set_namespace_for_node(canonicalization_method_node, DS_NAMESPACE, ds_namespace_prefix) signature_method_node = Nokogiri::XML::Node.new('SignatureMethod', document) signature_method_node['Algorithm'] = self.signature_algorithm_id node.add_child(signature_method_node) set_namespace_for_node(signature_method_node, DS_NAMESPACE, ds_namespace_prefix) end node end |
#to_xml ⇒ Object
75 76 77 |
# File 'lib/signer.rb', line 75 def to_xml document.to_xml(save_with: 0) end |
#x509_data_node(issuer_in_security_token = false) ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/signer.rb', line 227 def x509_data_node(issuer_in_security_token = false) issuer_name_node = Nokogiri::XML::Node.new('X509IssuerName', document) issuer_name_node.content = cert.issuer.to_s(OpenSSL::X509::Name::RFC2253) issuer_number_node = Nokogiri::XML::Node.new('X509SerialNumber', document) issuer_number_node.content = cert.serial issuer_serial_node = Nokogiri::XML::Node.new('X509IssuerSerial', document) issuer_serial_node.add_child(issuer_name_node) issuer_serial_node.add_child(issuer_number_node) cetificate_node = Nokogiri::XML::Node.new('X509Certificate', document) cetificate_node.content = Base64.encode64(cert.to_der).delete("\n") data_node = Nokogiri::XML::Node.new('X509Data', document) data_node.add_child(issuer_serial_node) data_node.add_child(cetificate_node) if issuer_in_security_token security_token_reference_node = Nokogiri::XML::Node.new("wsse:SecurityTokenReference", document) security_token_reference_node.add_child(data_node) end key_info_node = Nokogiri::XML::Node.new('KeyInfo', document) key_info_node.add_child(issuer_in_security_token ? security_token_reference_node : data_node) signed_info_node.add_next_sibling(key_info_node) set_namespace_for_node(key_info_node, DS_NAMESPACE, ds_namespace_prefix) set_namespace_for_node(security_token_reference_node, WSSE_NAMESPACE, ds_namespace_prefix) if issuer_in_security_token set_namespace_for_node(data_node, DS_NAMESPACE, ds_namespace_prefix) set_namespace_for_node(issuer_serial_node, DS_NAMESPACE, ds_namespace_prefix) set_namespace_for_node(cetificate_node, DS_NAMESPACE, ds_namespace_prefix) set_namespace_for_node(issuer_name_node, DS_NAMESPACE, ds_namespace_prefix) set_namespace_for_node(issuer_number_node, DS_NAMESPACE, ds_namespace_prefix) data_node end |