Class: Kiji::Signer
- Inherits:
-
Object
- Object
- Kiji::Signer
- Defined in:
- lib/kiji/signer.rb
Constant Summary collapse
- WSU_NAMESPACE =
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
- WSSE_NAMESPACE =
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
Instance Attribute Summary collapse
-
#cert ⇒ Object
Returns the value of attribute cert.
-
#document ⇒ Object
Returns the value of attribute document.
-
#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
<Signature xmlns=“www.w3.org/2000/09/xmldsig#”>.
Instance Method Summary collapse
-
#binary_security_token_node ⇒ Object
<o:BinarySecurityToken u:Id=“” ValueType=“docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3” EncodingType=“docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary”> …
- #canonicalize(node = document, inclusive_namespaces = nil) ⇒ 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).
- #digest_file!(file_content, options = {}) ⇒ Object
-
#initialize(document) {|_self| ... } ⇒ 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
<SignedInfo> <CanonicalizationMethod Algorithm=“www.w3.org/TR/2001/REC-xml-c14n-20010315”/> <SignatureMethod Algorithm=“www.w3.org/2000/09/xmldsig#rsa-sha1”/> …
- #to_xml ⇒ Object
-
#x509_data_node ⇒ Object
<KeyInfo> <X509Data> <X509Certificate>MIID+jCCAuKgAwIBA…</X509Certificate> </X509Data> </KeyInfo>.
Constructor Details
#initialize(document) {|_self| ... } ⇒ Signer
Returns a new instance of Signer.
20 21 22 23 24 25 26 27 |
# File 'lib/kiji/signer.rb', line 20 def initialize(document) # self.document = Nokogiri::XML(document.to_s, &:noblanks) self.document = Nokogiri::XML(document.to_s) self.digest_algorithm = :sha1 self.set_default_signature_method! yield(self) if block_given? end |
Instance Attribute Details
#cert ⇒ Object
Returns the value of attribute cert.
14 15 16 |
# File 'lib/kiji/signer.rb', line 14 def cert @cert end |
#document ⇒ Object
Returns the value of attribute document.
13 14 15 |
# File 'lib/kiji/signer.rb', line 13 def document @document end |
#private_key ⇒ Object
Returns the value of attribute private_key.
13 14 15 |
# File 'lib/kiji/signer.rb', line 13 def private_key @private_key end |
#security_node ⇒ Object
77 78 79 |
# File 'lib/kiji/signer.rb', line 77 def security_node @security_node ||= document.xpath('//wsse:Security', wsse: WSSE_NAMESPACE).first end |
#security_token_id ⇒ Object
73 74 75 |
# File 'lib/kiji/signer.rb', line 73 def security_token_id @security_token_id ||= 'uuid-639b8970-7644-4f9e-9bc4-9c2e367808fc-1' end |
#signature_algorithm_id ⇒ Object
Returns the value of attribute signature_algorithm_id.
13 14 15 |
# File 'lib/kiji/signer.rb', line 13 def signature_algorithm_id @signature_algorithm_id end |
#signature_node ⇒ Object
<Signature xmlns=“www.w3.org/2000/09/xmldsig#”>
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/kiji/signer.rb', line 87 def signature_node @signature_node ||= begin @signature_node = security_node.at_xpath('ds:Signature', ds: 'http://www.w3.org/2000/09/xmldsig#') unless @signature_node @signature_node = Nokogiri::XML::Node.new('Signature', document) @signature_node['Id'] = DateTime.now.strftime('%Y%m%d%H%M%S') @signature_node.default_namespace = 'http://www.w3.org/2000/09/xmldsig#' security_node.add_child(@signature_node) end @signature_node end end |
Instance Method Details
#binary_security_token_node ⇒ Object
<o:BinarySecurityToken u:Id=“” ValueType=“docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3” EncodingType=“docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary”>
...
</o:BinarySecurityToken> <SignedInfo>
...
</SignedInfo> <KeyInfo>
<o:SecurityTokenReference>
<o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#uuid-639b8970-7644-4f9e-9bc4-9c2e367808fc-1"/>
</o:SecurityTokenReference>
</KeyInfo>
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/kiji/signer.rb', line 131 def binary_security_token_node 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) 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) ⇒ Object
81 82 83 84 |
# File 'lib/kiji/signer.rb', line 81 def canonicalize(node = document, inclusive_namespaces = nil) # node.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0, inclusive_namespaces, nil) # The last argument should be exactly +nil+ to remove comments from result node.canonicalize(Nokogiri::XML::XML_C14N_1_1, inclusive_namespaces, nil) # The last argument should be exactly +nil+ to remove comments from result 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
Example of XML that will be inserted in message for call like digest!(node, inclusive_namespaces: ['soap'])
:
<Reference URI="#_0">
<Transforms>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soap" />
</Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>aeqXriJuUCk4tPNPAGDXGqHj6ao=</DigestValue>
</Reference>
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/kiji/signer.rb', line 206 def digest!(target_node, = {}) 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)}" # if id.to_s.size > 0 # wsu_ns ||= namespace_prefix(target_node, WSU_NAMESPACE, 'wsu') # target_node["#{wsu_ns}:Id"] = id.to_s # end target_canon = canonicalize(target_node, [:inclusive_namespaces]) # target_digest = Base64.encode64(@digester.digest(target_canon)).strip target_digest = @digester.base64(target_canon) reference_node = Nokogiri::XML::Node.new('Reference', document) reference_node['URI'] = id.to_s.size > 0 ? encode_ja(id) : '' signed_info_node.add_child(reference_node) transforms_node = Nokogiri::XML::Node.new('Transforms', document) reference_node.add_child(transforms_node) transform_node = Nokogiri::XML::Node.new('Transform', document) if [:enveloped] transform_node['Algorithm'] = 'http://www.w3.org/2000/09/xmldsig#enveloped-signature' else # transform_node['Algorithm'] = 'http://www.w3.org/2001/10/xml-exc-c14n#' transform_node['Algorithm'] = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' end if [:inclusive_namespaces] inclusive_namespaces_node = Nokogiri::XML::Node.new('ec:InclusiveNamespaces', document) inclusive_namespaces_node.add_namespace_definition('ec', transform_node['Algorithm']) inclusive_namespaces_node['PrefixList'] = [:inclusive_namespaces].join(' ') transform_node.add_child(inclusive_namespaces_node) end transforms_node.add_child(transform_node) digest_method_node = Nokogiri::XML::Node.new('DigestMethod', document) digest_method_node['Algorithm'] = @digester.digest_id reference_node.add_child(digest_method_node) digest_value_node = Nokogiri::XML::Node.new('DigestValue', document) digest_value_node.content = target_digest reference_node.add_child(digest_value_node) self end |
#digest_algorithm ⇒ Object
Return symbol name for supported digest algorithms and string name for custom ones.
34 35 36 |
# File 'lib/kiji/signer.rb', line 34 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.
43 44 45 |
# File 'lib/kiji/signer.rb', line 43 def digest_algorithm=(algorithm) @digester = Kiji::Digester.new(algorithm) end |
#digest_file!(file_content, options = {}) ⇒ Object
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/kiji/signer.rb', line 250 def digest_file!(file_content, = {}) # target_digest = Base64.encode64(@digester.digest(file_content)).strip target_digest = @digester.base64(file_content) reference_node = Nokogiri::XML::Node.new('Reference', document) id = [:id] reference_node['URI'] = id.to_s.size > 0 ? encode_ja(id) : '' signed_info_node.add_child(reference_node) digest_method_node = Nokogiri::XML::Node.new('DigestMethod', document) digest_method_node['Algorithm'] = @digester.digest_id reference_node.add_child(digest_method_node) digest_value_node = Nokogiri::XML::Node.new('DigestValue', document) digest_value_node.content = target_digest reference_node.add_child(digest_value_node) self 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
:inclusive_namespaces
-
Array of namespace prefixes which definitions should be added to signed info node during canonicalization
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/kiji/signer.rb', line 279 def sign!( = {}) binary_security_token_node if [:security_token] x509_data_node if [:issuer_serial] if [:inclusive_namespaces] c14n_method_node = signed_info_node.at_xpath('ds:CanonicalizationMethod', ds: 'http://www.w3.org/2000/09/xmldsig#') 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).gsub("\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) self end |
#signature_digest_algorithm ⇒ Object
Return symbol name for supported digest algorithms and string name for custom ones.
48 49 50 |
# File 'lib/kiji/signer.rb', line 48 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=
53 54 55 |
# File 'lib/kiji/signer.rb', line 53 def signature_digest_algorithm=(algorithm) @sign_digester = Kiji::Digester.new(algorithm) end |
#signed_info_node ⇒ Object
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
...
</SignedInfo>
105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/kiji/signer.rb', line 105 def signed_info_node node = signature_node.at_xpath('ds:SignedInfo', ds: 'http://www.w3.org/2000/09/xmldsig#') unless node node = Nokogiri::XML::Node.new('SignedInfo', document) signature_node.add_child(node) canonicalization_method_node = Nokogiri::XML::Node.new('CanonicalizationMethod', document) canonicalization_method_node['Algorithm'] = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' node.add_child(canonicalization_method_node) signature_method_node = Nokogiri::XML::Node.new('SignatureMethod', document) signature_method_node['Algorithm'] = signature_algorithm_id node.add_child(signature_method_node) end node end |
#to_xml ⇒ Object
29 30 31 |
# File 'lib/kiji/signer.rb', line 29 def to_xml document.to_xml(save_with: 0, encoding: 'UTF-8') end |
#x509_data_node ⇒ Object
<KeyInfo>
<X509Data>
<X509Certificate>MIID+jCCAuKgAwIBA...</X509Certificate>
</X509Data>
</KeyInfo>
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/kiji/signer.rb', line 159 def x509_data_node # issuer_name_node = Nokogiri::XML::Node.new('X509IssuerName', document) # issuer_name_node.content = "System.Security.Cryptography.X509Certificates.X500DistinguishedName" # # 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).gsub("\n", '') data_node = Nokogiri::XML::Node.new('X509Data', document) # data_node.add_child(issuer_serial_node) data_node.add_child(cetificate_node) key_info_node = Nokogiri::XML::Node.new('KeyInfo', document) key_info_node.add_child(data_node) signed_info_node.add_next_sibling(key_info_node) data_node end |