eet_signer 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e069ff06ff3147ba7f9520b38827bf504a40421f
4
+ data.tar.gz: 5f6e4549a1253d3c2424ca42223cb36554595a9e
5
+ SHA512:
6
+ metadata.gz: cee8321399e31116b5bea49e1cde1466c30e983b3dbee449dd6cf63667857f16151a5fefd668c0eb503cdee4e27d436b598766391fd49a1d59614154f8704a36
7
+ data.tar.gz: 50f2844ea8a306237e5d8ece4023259fe7fe3db4a362c47e48c613a57ac37965f5c9e3b8a12403739f4880c251f257e7ef570488d96049b62cbf9da88059ef14
data/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ ## 1.5.0 (2017-01-23)
2
+
3
+ - Add posibility to disable noblanks method in Signer initialization (#16, @bpietraga)
4
+ - Minimum ruby version is now 2.1
5
+
6
+ ## 1.4.3 (2015-10-28)
7
+
8
+ - Fixed Issuer Name node (#8, @tiagocasanovapt)
9
+
10
+ ## 1.4.2 (2014-11-30)
11
+
12
+ - Fixed behaviour on XMLs that already contains nested signatures somewhere
13
+
14
+ ## 1.4.1 (2014-09-09)
15
+
16
+ - Changed method of getting GOST R 34.11-94 digest algorithm to more short and generic (and working in Ubuntu 14.04 and other OS)
17
+
18
+ ## 1.4.0 (2014-06-24)
19
+
20
+ - Support signing and digesting with inclusive namespaces (#5, @Envek)
21
+
22
+ ## 1.3.1 (2014-06-24)
23
+
24
+ - Fix namespace issue for SecurityTokenReference tag (#4, #@Envek)
25
+
26
+ ## 1.3.0 (2014-06-16)
27
+
28
+ - Allow to sign with other digest algorithms - SHA1, SHA256, and GOST R 34.11-94 (#3, @Envek)
29
+
30
+ ## 1.2.1 (2014-05-14)
31
+
32
+ - Fix canonicalization: should be without comments (#2, @Envek)
33
+
34
+ ## 1.2.0 (2014-05-06)
35
+
36
+ - Id and attribute namespace preserving when digesting the nodes (#1, @Envek)
37
+
38
+ ## 1.1.1 (2013-04-03)
39
+
40
+ - Allow to sign using enveloped-signature
41
+
42
+ ## 1.1.0 (2012-06-21)
43
+
44
+ - Allow to sign XML documents without SOAP
45
+
46
+ ## 1.0.0 (2012-05-03)
47
+
48
+ - Allow to sign SOAP documents
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Edgars Beigarts
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Signer
2
+
3
+ This is a fork of original [Signer](https://badge.fury.io/rb/signer.svg) gem with applied patch for explicit signature namespaces.
4
+
5
+ ## Why?
6
+
7
+ We needed the explicit namespaces feature for other gems and it's much easier to do so with normal ruby gem rather than github fork.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ gem install eet_signer
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ For usage please see original gem's documentation. This gem should be rebased with the original gem at regularly.
18
+
19
+ ### Explicit signature namespaces
20
+
21
+ If you need `Signature` tags to be in explicit namespace (say, `<ds:Signature>`) instead of to be in implicit default namespace you can specify next option:
22
+
23
+ ```ruby
24
+ signer.ds_namespace_prefix = 'ds'
25
+ ```
@@ -0,0 +1,69 @@
1
+ require 'openssl'
2
+
3
+ class Signer
4
+
5
+ # Digest algorithms supported "out of the box"
6
+ DIGEST_ALGORITHMS = {
7
+ # SHA 1
8
+ sha1: {
9
+ name: 'SHA1',
10
+ id: 'http://www.w3.org/2000/09/xmldsig#sha1',
11
+ digester: lambda { OpenSSL::Digest::SHA1.new },
12
+ },
13
+ # SHA 256
14
+ sha256: {
15
+ name: 'SHA256',
16
+ id: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
17
+ digester: lambda { OpenSSL::Digest::SHA256.new },
18
+ },
19
+ # GOST R 34-11 94
20
+ gostr3411: {
21
+ name: 'GOST R 34.11-94',
22
+ id: 'http://www.w3.org/2001/04/xmldsig-more#gostr3411',
23
+ digester: lambda { OpenSSL::Digest.new('md_gost94') },
24
+ },
25
+ }
26
+
27
+ # Class that holds +OpenSSL::Digest+ instance with some meta information for digesting in XML.
28
+ class Digester
29
+
30
+ # You may pass either a one of +:sha1+, +:sha256+ or +:gostr3411+ symbols
31
+ # or +Hash+ with keys +:id+ with a string, which will denote algorithm in XML Reference tag
32
+ # and +:digester+ with instance of class with interface compatible with +OpenSSL::Digest+ class.
33
+ def initialize(algorithm)
34
+ if algorithm.kind_of? Symbol
35
+ @digest_info = DIGEST_ALGORITHMS[algorithm].dup
36
+ @digest_info[:digester] = @digest_info[:digester].call
37
+ @symbol = algorithm
38
+ else
39
+ @digest_info = algorithm
40
+ end
41
+ end
42
+
43
+ attr_reader :symbol
44
+
45
+ # Digest
46
+ def digest(message)
47
+ self.digester.digest(message)
48
+ end
49
+
50
+ alias call digest
51
+
52
+ # Returns +OpenSSL::Digest+ (or derived class) instance
53
+ def digester
54
+ @digest_info[:digester].reset
55
+ end
56
+
57
+ # Human-friendly name
58
+ def digest_name
59
+ @digest_info[:name]
60
+ end
61
+
62
+ # XML-friendly name (for specifying in XML +DigestMethod+ node +Algorithm+ attribute)
63
+ def digest_id
64
+ @digest_info[:id]
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,3 @@
1
+ class Signer
2
+ VERSION = '1.5.0'
3
+ end
data/lib/signer.rb ADDED
@@ -0,0 +1,336 @@
1
+ require "nokogiri"
2
+ require "base64"
3
+ require "digest/sha1"
4
+ require "openssl"
5
+
6
+ require "signer/digester"
7
+ require "signer/version"
8
+
9
+ class Signer
10
+ attr_accessor :document, :private_key, :signature_algorithm_id, :ds_namespace_prefix
11
+ attr_reader :cert
12
+ attr_writer :security_node, :signature_node, :security_token_id
13
+
14
+ WSU_NAMESPACE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
15
+ WSSE_NAMESPACE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
16
+ DS_NAMESPACE = 'http://www.w3.org/2000/09/xmldsig#'
17
+
18
+ def initialize(document, noblanks: true)
19
+ self.document = Nokogiri::XML(document.to_s) do |config|
20
+ config.noblanks if noblanks
21
+ end
22
+ self.digest_algorithm = :sha1
23
+ self.set_default_signature_method!
24
+ end
25
+
26
+ def to_xml
27
+ document.to_xml(:save_with => 0)
28
+ end
29
+
30
+ # Return symbol name for supported digest algorithms and string name for custom ones.
31
+ def digest_algorithm
32
+ @digester.symbol || @digester.digest_name
33
+ end
34
+
35
+ # Allows to change algorithm for node digesting (default is SHA1).
36
+ #
37
+ # You may pass either a one of +:sha1+, +:sha256+ or +:gostr3411+ symbols
38
+ # or +Hash+ with keys +:id+ with a string, which will denote algorithm in XML Reference tag
39
+ # and +:digester+ with instance of class with interface compatible with +OpenSSL::Digest+ class.
40
+ def digest_algorithm=(algorithm)
41
+ @digester = Signer::Digester.new(algorithm)
42
+ end
43
+
44
+ # Return symbol name for supported digest algorithms and string name for custom ones.
45
+ def signature_digest_algorithm
46
+ @sign_digester.symbol || @sign_digester.digest_name
47
+ end
48
+
49
+ # Allows to change digesting algorithm for signature creation. Same as +digest_algorithm=+
50
+ def signature_digest_algorithm=(algorithm)
51
+ @sign_digester = Signer::Digester.new(algorithm)
52
+ end
53
+
54
+ # Receives certificate for signing and tries to guess a digest algorithm for signature creation.
55
+ #
56
+ # Will change +signature_digest_algorithm+ and +signature_algorithm_id+ for known certificate types and reset to defaults for others.
57
+ def cert=(certificate)
58
+ @cert = certificate
59
+ # Try to guess a digest algorithm for signature creation
60
+ case @cert.signature_algorithm
61
+ when 'GOST R 34.11-94 with GOST R 34.10-2001'
62
+ self.signature_digest_algorithm = :gostr3411
63
+ self.signature_algorithm_id = 'http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411'
64
+ # Add clauses for other types of keys that require other digest algorithms and identifiers
65
+ else # most common 'sha1WithRSAEncryption' type included here
66
+ self.set_default_signature_method! # Reset any changes as they can become malformed
67
+ end
68
+ end
69
+
70
+ def security_token_id
71
+ @security_token_id ||= "uuid-639b8970-7644-4f9e-9bc4-9c2e367808fc-1"
72
+ end
73
+
74
+ def security_node
75
+ @security_node ||= document.xpath('//wsse:Security', wsse: WSSE_NAMESPACE).first
76
+ end
77
+
78
+ def canonicalize(node = document, inclusive_namespaces=nil)
79
+ node.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0, inclusive_namespaces, nil) # The last argument should be exactly +nil+ to remove comments from result
80
+ end
81
+
82
+ # <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
83
+ def signature_node
84
+ @signature_node ||= begin
85
+ @signature_node = security_node.at_xpath('ds:Signature', ds: DS_NAMESPACE)
86
+ unless @signature_node
87
+ @signature_node = Nokogiri::XML::Node.new('Signature', document)
88
+ set_namespace_for_node(@signature_node, DS_NAMESPACE, ds_namespace_prefix)
89
+ security_node.add_child(@signature_node)
90
+ end
91
+ @signature_node
92
+ end
93
+ end
94
+
95
+ # <SignedInfo>
96
+ # <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
97
+ # <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
98
+ # ...
99
+ # </SignedInfo>
100
+ def signed_info_node
101
+ node = signature_node.at_xpath('ds:SignedInfo', ds: DS_NAMESPACE)
102
+ unless node
103
+ node = Nokogiri::XML::Node.new('SignedInfo', document)
104
+ signature_node.add_child(node)
105
+ set_namespace_for_node(node, DS_NAMESPACE, ds_namespace_prefix)
106
+ canonicalization_method_node = Nokogiri::XML::Node.new('CanonicalizationMethod', document)
107
+ canonicalization_method_node['Algorithm'] = 'http://www.w3.org/2001/10/xml-exc-c14n#'
108
+ node.add_child(canonicalization_method_node)
109
+ set_namespace_for_node(canonicalization_method_node, DS_NAMESPACE, ds_namespace_prefix)
110
+ signature_method_node = Nokogiri::XML::Node.new('SignatureMethod', document)
111
+ signature_method_node['Algorithm'] = self.signature_algorithm_id
112
+ node.add_child(signature_method_node)
113
+ set_namespace_for_node(signature_method_node, DS_NAMESPACE, ds_namespace_prefix)
114
+ end
115
+ node
116
+ end
117
+
118
+ # <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">
119
+ # ...
120
+ # </o:BinarySecurityToken>
121
+ # <SignedInfo>
122
+ # ...
123
+ # </SignedInfo>
124
+ # <KeyInfo>
125
+ # <o:SecurityTokenReference>
126
+ # <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"/>
127
+ # </o:SecurityTokenReference>
128
+ # </KeyInfo>
129
+ def binary_security_token_node
130
+ node = document.at_xpath('wsse:BinarySecurityToken', wsse: WSSE_NAMESPACE)
131
+ unless node
132
+ node = Nokogiri::XML::Node.new('BinarySecurityToken', document)
133
+ node['ValueType'] = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'
134
+ node['EncodingType'] = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'
135
+ node.content = Base64.encode64(cert.to_der).gsub("\n", '')
136
+ signature_node.add_previous_sibling(node)
137
+ wsse_ns = namespace_prefix(node, WSSE_NAMESPACE, 'wsse')
138
+ wsu_ns = namespace_prefix(node, WSU_NAMESPACE, 'wsu')
139
+ node["#{wsu_ns}:Id"] = security_token_id
140
+ key_info_node = Nokogiri::XML::Node.new('KeyInfo', document)
141
+ security_token_reference_node = Nokogiri::XML::Node.new("#{wsse_ns}:SecurityTokenReference", document)
142
+ key_info_node.add_child(security_token_reference_node)
143
+ set_namespace_for_node(key_info_node, DS_NAMESPACE, ds_namespace_prefix)
144
+ reference_node = Nokogiri::XML::Node.new("#{wsse_ns}:Reference", document)
145
+ reference_node['ValueType'] = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'
146
+ reference_node['URI'] = "##{security_token_id}"
147
+ security_token_reference_node.add_child(reference_node)
148
+ signed_info_node.add_next_sibling(key_info_node)
149
+ end
150
+ node
151
+ end
152
+
153
+ # <KeyInfo>
154
+ # <X509Data>
155
+ # <X509IssuerSerial>
156
+ # <X509IssuerName>System.Security.Cryptography.X509Certificates.X500DistinguishedName</X509IssuerName>
157
+ # <X509SerialNumber>13070789</X509SerialNumber>
158
+ # </X509IssuerSerial>
159
+ # <X509Certificate>MIID+jCCAuKgAwIBAgIEAMdxxTANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQGEwJTRTEeMBwGA1UEChMVTm9yZGVhIEJhbmsgQUIgKHB1YmwpMScwJQYDVQQDEx5Ob3JkZWEgcm9sZS1jZXJ0aWZpY2F0ZXMgQ0EgMDExFDASBgNVBAUTCzUxNjQwNi0wMTIwMB4XDTA5MDYxMTEyNTAxOVoXDTExMDYxMTEyNTAxOVowcjELMAkGA1UEBhMCU0UxIDAeBgNVBAMMF05vcmRlYSBEZW1vIENlcnRpZmljYXRlMRQwEgYDVQQEDAtDZXJ0aWZpY2F0ZTEUMBIGA1UEKgwLTm9yZGVhIERlbW8xFTATBgNVBAUTDDAwOTU1NzI0Mzc3MjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwcgz5AzbxTbsCE51No7fPnSqmQBIMW9OiPkiHotwYQTl+H9qwDvQRyBqHN26tnw7hNvEShd1ZRGUg4drMEXDV5CmKqsAevs9lauWDaHnGKPNHZJ1hNNYXHwymksEz5zMnG8eqRdhb4vOV2FzreJeYpsgx31Bv0aTofHcHVz4uGcCAwEAAaOCASAwggEcMAkGA1UdEwQCMAAwEQYDVR0OBAoECEj6Y9/vU03WMBMGA1UdIAQMMAowCAYGKoVwRwEDMBMGA1UdIwQMMAqACEIFjfLBeTpRMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3Aubm9yZGVhLnNlL1JDQTAxMA4GA1UdDwEB/wQEAwIGQDCBiAYDVR0fBIGAMH4wfKB6oHiGdmxkYXA6Ly9sZGFwLm5iLnNlL2NuPU5vcmRlYSUyMHJvbGUtY2VydGlmaWNhdGVzJTIwQ0ElMjAwMSxvPU5vcmRlYSUyMEJhbmslMjBBQiUyMChwdWJsKSxjPVNFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwDQYJKoZIhvcNAQEFBQADggEBAEXUv87VpHk51y3TqkMb1MYDqeKvQRE1cNcvhEJhIzdDpXMA9fG0KqvSTT1e0ZI2r78mXDvtTZnpic44jX2XMSmKO6n+1taAXq940tJUhF4arYMUxwDKOso0Doanogug496gipqMlpLgvIhGt06sWjNrvHzp2eGydUFdCsLr2ULqbDcut7g6eMcmrsnrOntjEU/J3hO8gyCeldJ+fI81qarrK/I0MZLR5LWCyVG/SKduoxHLX7JohsbIGyK1qAh9fi8l6X1Rcu80v5inpu71E/DnjbkAZBo7vsj78zzdk7KNliBIqBcIszdJ3dEHRWSI7FspRxyiR0NDm4lpyLwFtfw=</X509Certificate>
160
+ # </X509Data>
161
+ # </KeyInfo>
162
+ def x509_data_node
163
+ issuer_name_node = Nokogiri::XML::Node.new('X509IssuerName', document)
164
+ issuer_name_node.content = cert.issuer.to_s[1..-1].gsub(/\//, ',')
165
+
166
+ issuer_number_node = Nokogiri::XML::Node.new('X509SerialNumber', document)
167
+ issuer_number_node.content = cert.serial
168
+
169
+ issuer_serial_node = Nokogiri::XML::Node.new('X509IssuerSerial', document)
170
+ issuer_serial_node.add_child(issuer_name_node)
171
+ issuer_serial_node.add_child(issuer_number_node)
172
+
173
+ cetificate_node = Nokogiri::XML::Node.new('X509Certificate', document)
174
+ cetificate_node.content = Base64.encode64(cert.to_der).gsub("\n", '')
175
+
176
+ data_node = Nokogiri::XML::Node.new('X509Data', document)
177
+ data_node.add_child(issuer_serial_node)
178
+ data_node.add_child(cetificate_node)
179
+
180
+ key_info_node = Nokogiri::XML::Node.new('KeyInfo', document)
181
+ key_info_node.add_child(data_node)
182
+
183
+ signed_info_node.add_next_sibling(key_info_node)
184
+
185
+ set_namespace_for_node(key_info_node, DS_NAMESPACE, ds_namespace_prefix)
186
+ set_namespace_for_node(data_node, DS_NAMESPACE, ds_namespace_prefix)
187
+ set_namespace_for_node(issuer_serial_node, DS_NAMESPACE, ds_namespace_prefix)
188
+ set_namespace_for_node(cetificate_node, DS_NAMESPACE, ds_namespace_prefix)
189
+ set_namespace_for_node(issuer_name_node, DS_NAMESPACE, ds_namespace_prefix)
190
+ set_namespace_for_node(issuer_number_node, DS_NAMESPACE, ds_namespace_prefix)
191
+
192
+ data_node
193
+ end
194
+
195
+ ##
196
+ # Digests some +target_node+, which integrity you wish to track. Any changes in digested node will invalidate signed message.
197
+ # All digest should be calculated **before** signing.
198
+ #
199
+ # Available options:
200
+ # * [+:id+] Id for the node, if you don't want to use automatically calculated one
201
+ # * [+:inclusive_namespaces+] Array of namespace prefixes which definitions should be added to node during canonicalization
202
+ # * [+:enveloped+]
203
+ #
204
+ # Example of XML that will be inserted in message for call like <tt>digest!(node, inclusive_namespaces: ['soap'])</tt>:
205
+ #
206
+ # <Reference URI="#_0">
207
+ # <Transforms>
208
+ # <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
209
+ # <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soap" />
210
+ # </Transform>
211
+ # </Transforms>
212
+ # <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
213
+ # <DigestValue>aeqXriJuUCk4tPNPAGDXGqHj6ao=</DigestValue>
214
+ # </Reference>
215
+
216
+ def digest!(target_node, options = {})
217
+ wsu_ns = namespace_prefix(target_node, WSU_NAMESPACE)
218
+ current_id = target_node["#{wsu_ns}:Id"] if wsu_ns
219
+ id = options[:id] || current_id || "_#{Digest::SHA1.hexdigest(target_node.to_s)}"
220
+ if id.to_s.size > 0
221
+ wsu_ns ||= namespace_prefix(target_node, WSU_NAMESPACE, 'wsu')
222
+ target_node["#{wsu_ns}:Id"] = id.to_s
223
+ end
224
+ target_canon = canonicalize(target_node, options[:inclusive_namespaces])
225
+ target_digest = Base64.encode64(@digester.digest(target_canon)).strip
226
+
227
+ reference_node = Nokogiri::XML::Node.new('Reference', document)
228
+ reference_node['URI'] = id.to_s.size > 0 ? "##{id}" : ""
229
+ signed_info_node.add_child(reference_node)
230
+ set_namespace_for_node(reference_node, DS_NAMESPACE, ds_namespace_prefix)
231
+
232
+ transforms_node = Nokogiri::XML::Node.new('Transforms', document)
233
+ reference_node.add_child(transforms_node)
234
+ set_namespace_for_node(transforms_node, DS_NAMESPACE, ds_namespace_prefix)
235
+
236
+ transform_node = Nokogiri::XML::Node.new('Transform', document)
237
+ set_namespace_for_node(transform_node, DS_NAMESPACE, ds_namespace_prefix)
238
+ if options[:enveloped]
239
+ transform_node['Algorithm'] = 'http://www.w3.org/2000/09/xmldsig#enveloped-signature'
240
+ else
241
+ transform_node['Algorithm'] = 'http://www.w3.org/2001/10/xml-exc-c14n#'
242
+ end
243
+ if options[:inclusive_namespaces]
244
+ inclusive_namespaces_node = Nokogiri::XML::Node.new('ec:InclusiveNamespaces', document)
245
+ inclusive_namespaces_node.add_namespace_definition('ec', transform_node['Algorithm'])
246
+ inclusive_namespaces_node['PrefixList'] = options[:inclusive_namespaces].join(' ')
247
+ transform_node.add_child(inclusive_namespaces_node)
248
+ end
249
+ transforms_node.add_child(transform_node)
250
+
251
+ digest_method_node = Nokogiri::XML::Node.new('DigestMethod', document)
252
+ digest_method_node['Algorithm'] = @digester.digest_id
253
+ reference_node.add_child(digest_method_node)
254
+ set_namespace_for_node(digest_method_node, DS_NAMESPACE, ds_namespace_prefix)
255
+
256
+ digest_value_node = Nokogiri::XML::Node.new('DigestValue', document)
257
+ digest_value_node.content = target_digest
258
+ reference_node.add_child(digest_value_node)
259
+ set_namespace_for_node(digest_value_node, DS_NAMESPACE, ds_namespace_prefix)
260
+ self
261
+ end
262
+
263
+ ##
264
+ # Sign document with provided certificate, private key and other options
265
+ #
266
+ # This should be very last action before calling +to_xml+, all the required nodes should be digested with +digest!+ **before** signing.
267
+ #
268
+ # Available options:
269
+ # * [+:security_token+] Serializes certificate in DER format, encodes it with Base64 and inserts it within +<BinarySecurityToken>+ tag
270
+ # * [+:issuer_serial+]
271
+ # * [+:inclusive_namespaces+] Array of namespace prefixes which definitions should be added to signed info node during canonicalization
272
+
273
+ def sign!(options = {})
274
+ if options[:security_token]
275
+ binary_security_token_node
276
+ end
277
+
278
+ if options[:issuer_serial]
279
+ x509_data_node
280
+ end
281
+
282
+ if options[:inclusive_namespaces]
283
+ c14n_method_node = signed_info_node.at_xpath('ds:CanonicalizationMethod', ds: DS_NAMESPACE)
284
+ inclusive_namespaces_node = Nokogiri::XML::Node.new('ec:InclusiveNamespaces', document)
285
+ inclusive_namespaces_node.add_namespace_definition('ec', c14n_method_node['Algorithm'])
286
+ inclusive_namespaces_node['PrefixList'] = options[:inclusive_namespaces].join(' ')
287
+ c14n_method_node.add_child(inclusive_namespaces_node)
288
+ end
289
+
290
+ signed_info_canon = canonicalize(signed_info_node, options[:inclusive_namespaces])
291
+
292
+ signature = private_key.sign(@sign_digester.digester, signed_info_canon)
293
+ signature_value_digest = Base64.encode64(signature).gsub("\n", '')
294
+
295
+ signature_value_node = Nokogiri::XML::Node.new('SignatureValue', document)
296
+ signature_value_node.content = signature_value_digest
297
+ signed_info_node.add_next_sibling(signature_value_node)
298
+ set_namespace_for_node(signature_value_node, DS_NAMESPACE, ds_namespace_prefix)
299
+ self
300
+ end
301
+
302
+ protected
303
+
304
+ # Reset digest algorithm for signature creation and signature algorithm identifier
305
+ def set_default_signature_method!
306
+ self.signature_digest_algorithm = :sha1
307
+ self.signature_algorithm_id = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
308
+ end
309
+
310
+ ##
311
+ # Searches in namespaces, defined on +target_node+ or its ancestors,
312
+ # for the +namespace+ with given URI and returns its prefix.
313
+ #
314
+ # If there is no such namespace and +desired_prefix+ is specified,
315
+ # adds such a namespace to +target_node+ with +desired_prefix+
316
+
317
+ def namespace_prefix(target_node, namespace, desired_prefix = nil)
318
+ ns = target_node.namespaces.key(namespace)
319
+ if ns
320
+ ns.match(/(?:xmlns:)?(.*)/) && $1
321
+ elsif desired_prefix
322
+ target_node.add_namespace_definition(desired_prefix, namespace)
323
+ desired_prefix
324
+ end
325
+ end
326
+
327
+ ##
328
+ # Searches for namespace with given +href+ within +node+ ancestors and assigns it to this node.
329
+ # If there is no such namespace, it will create it with +desired_prefix+ if present or as default namespace otherwise.
330
+ # In most cases you should insert +node+ in the document tree before calling this method to avoid duplicating namespace definitions
331
+ def set_namespace_for_node(node, href, desired_prefix = nil)
332
+ return node.namespace if node.namespace && node.namespace.href == href # This node already in target namespace, done
333
+ namespace = node.namespace_scopes.find { |ns| ns.href == href }
334
+ node.namespace = namespace || node.add_namespace_definition(desired_prefix, href)
335
+ end
336
+ end
@@ -0,0 +1,17 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICsDCCAhmgAwIBAgIJAOUHvh4oho0tMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
3
+ BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
4
+ aWRnaXRzIFB0eSBMdGQwHhcNMTIwNTAzMTMxODIyWhcNMTMwNTAzMTMxODIyWjBF
5
+ MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
6
+ ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
7
+ gQCvK5hMPv/R5IFmwWyJOyEaFUrF/ZsmN+Gip8hvR6rLP3YPNx9iFYvPcZllFmuV
8
+ wyaz7YT2N5BsqTwLdyi5v4HY4fUtuz0p8jIPoSd6dfDvcnSpf4QLTOgOaL3ciPEb
9
+ gDHH2tnIksukoWzqCYva+qFZ74NFl19swXotW9fA4Jzs4QIDAQABo4GnMIGkMB0G
10
+ A1UdDgQWBBRU1WEHDnP8Hr7ZulxrSzEwOcYpMzB1BgNVHSMEbjBsgBRU1WEHDnP8
11
+ Hr7ZulxrSzEwOcYpM6FJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
12
+ U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAOUHvh4o
13
+ ho0tMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEASY/9SAOK57q9mGnN
14
+ JJeyDbmyGrAHSJTod646xTHYkMvhUqwHyk9PTr5bdfmswpmyVn+AQ43U2tU5vnpT
15
+ BmKpHWD2+HSHgGa92mMLrfBOd8EBZ329NL3N2HDPIaHr4NPGyhNrSK3QVOnAq2D0
16
+ jlyrGYJlLli1NxHiBz7FCEJaVI8=
17
+ -----END CERTIFICATE-----
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0"?>
2
+ <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:wsurandom="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
3
+ <s:Header>
4
+ <a:Action s:mustUnderstand="1">http://tempuri.org/IDocumentService/SearchDocuments</a:Action>
5
+ <a:MessageID>urn:uuid:30db5d4f-ab84-46be-907c-be690a92979b</a:MessageID>
6
+ <a:ReplyTo>
7
+ <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
8
+ </a:ReplyTo>
9
+ <To xmlns="http://www.w3.org/2005/08/addressing" xmlns:a="http://www.w3.org/2003/05/soap-envelope" a:mustUnderstand="1">http://tempuri.org/PublicServices/Test/1.0.12/PublicServices/DocumentService.svc</To>
10
+ <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
11
+ <wsurandom:Timestamp>
12
+ <wsurandom:Created>2012-05-02T18:17:14.467Z</wsurandom:Created>
13
+ <wsurandom:Expires>2012-05-02T18:22:14.467Z</wsurandom:Expires>
14
+ </wsurandom:Timestamp>
15
+ </wsse:Security>
16
+ </s:Header>
17
+ <s:Body>
18
+ <SearchDocuments xmlns="http://tempuri.org/">
19
+ <searchCriteria xmlns:b="http://schemas.datacontract.org/2004/07/BusinessLogic.Data.Documents.Integration" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
20
+ <b:RegistrationNo>1</b:RegistrationNo>
21
+ </searchCriteria>
22
+ </SearchDocuments>
23
+ </s:Body>
24
+ </s:Envelope>
@@ -0,0 +1,7 @@
1
+ <ApplicationRequest xmlns="http://bxd.fi/xmldata/">
2
+ <CustomerId>679155330</CustomerId>
3
+ <Command>GetUserInfo</Command>
4
+ <Timestamp>2010-05-10T13:22:19.847+03:00</Timestamp>
5
+ <Environment>PRODUCTION</Environment>
6
+ <SoftwareId>Petri</SoftwareId>
7
+ </ApplicationRequest>
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0"?>
2
+ <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
3
+ <s:Header>
4
+ <a:Action s:mustUnderstand="1">http://tempuri.org/IDocumentService/SearchDocuments</a:Action>
5
+ <a:MessageID>urn:uuid:30db5d4f-ab84-46be-907c-be690a92979b</a:MessageID>
6
+ <To xmlns="http://www.w3.org/2005/08/addressing" xmlns:a="http://www.w3.org/2003/05/soap-envelope" a:mustUnderstand="1">http://tempuri.org/PublicServices/Test/1.0.12/PublicServices/DocumentService.svc</To>
7
+ <o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
8
+ <u:Timestamp>
9
+ <u:Created>2012-05-02T18:17:14.467Z</u:Created>
10
+ <u:Expires>2012-05-02T18:22:14.467Z</u:Expires>
11
+ </u:Timestamp>
12
+ </o:Security>
13
+ </s:Header>
14
+ <s:Body>
15
+ <SearchDocuments xmlns="http://tempuri.org/">
16
+ <searchCriteria xmlns:b="http://schemas.datacontract.org/2004/07/BusinessLogic.Data.Documents.Integration" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
17
+ <!-- This comment shouldn't affect digest value for digested node -->
18
+ <b:RegistrationNo>1</b:RegistrationNo>
19
+ </searchCriteria>
20
+ </SearchDocuments>
21
+ </s:Body>
22
+ </s:Envelope>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0"?>
2
+ <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:wsurandom="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
3
+ <s:Header>
4
+ <a:Action s:mustUnderstand="1">http://tempuri.org/IDocumentService/SearchDocuments</a:Action>
5
+ <a:MessageID>urn:uuid:30db5d4f-ab84-46be-907c-be690a92979b</a:MessageID>
6
+ <a:ReplyTo>
7
+ <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
8
+ </a:ReplyTo>
9
+ <To xmlns="http://www.w3.org/2005/08/addressing" xmlns:a="http://www.w3.org/2003/05/soap-envelope" a:mustUnderstand="1">http://tempuri.org/PublicServices/Test/1.0.12/PublicServices/DocumentService.svc</To>
10
+ <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
11
+ <wsurandom:Timestamp>
12
+ <wsurandom:Created>2012-05-02T18:17:14.467Z</wsurandom:Created>
13
+ <wsurandom:Expires>2012-05-02T18:22:14.467Z</wsurandom:Expires>
14
+ </wsurandom:Timestamp>
15
+ </wsse:Security>
16
+ </s:Header>
17
+ <s:Body>
18
+ <SearchDocuments xmlns="http://tempuri.org/">
19
+ <searchCriteria xmlns:b="http://schemas.datacontract.org/2004/07/BusinessLogic.Data.Documents.Integration" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
20
+ <b:RegistrationNo>1</b:RegistrationNo>
21
+ </searchCriteria>
22
+ <ds:Signature />
23
+ </SearchDocuments>
24
+ </s:Body>
25
+ </s:Envelope>
@@ -0,0 +1,18 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ Proc-Type: 4,ENCRYPTED
3
+ DEK-Info: DES-EDE3-CBC,D53BEDA3FEA82258
4
+
5
+ a9x4N5JZ6qwza28OAEBWyS4s4GysgbiJf11wX2e1Ol/rHt9/PGvZCK9PSyW/haRn
6
+ l5iT95n8Ohyr16Ngx/p02Jq3TEbs/Jsxz7+z55R5E3LvGKGZIB8Yh8dvI4ArzPsK
7
+ QiwoegJnSWdTQnfybbbpv+11RrKtb1/H0/i9fIHDJUhch9HwNn/yZGs9uYRxVBGe
8
+ OFLAdnCB+JG5Q5p24aWeYEWQLbYB69uc9kiV60qgfp4ZxRgdeU55eLZe48d9clG7
9
+ 8vFAjM3+Q5WcgAgthKgD2HbjgcQ11WIZyHsshRvdWukk7iir7ogbOR/20sIWc+mY
10
+ hVdWfctYfWuheq7qwAfbvc8gSpgBRMziSI+5dFF4Ge80ahy7sxdlWJ4zNrqzTEIf
11
+ 5CyN2cC6h8fbJ95bifJFUIKDi+rpkmFtnW4eh11BOL6vYlSFaL35Kw2teRmFCk6M
12
+ iBmIStavcfVj61ZClla3Xso5gl8Ei8EGBSuSSLT1Uq3WJ6eLh5JWkpV3+mir1HWO
13
+ vNIl6L4zm2kngwL73NFeG7ZFMTv2yAuif3Hpr3akZudp/+qDcIcnqnmY47HsOtOb
14
+ ArLwBalIu4zsKF5IzqGiZ6tbMgRHDbLw/HAuuYHlJF5TeMDqnQFGi09LpO6IHPBh
15
+ ZqVKuZ1LVLgCRpvkW12z0sxfByFHKK6RvgPgR9tPdVmCBJLwCbLa7AqNDVrJNe6q
16
+ OEwp95+7OTx1b7jVXiZ3wO0XpPWKAMXWAEGDd9YeNVdfXPgypSe4p/wJgtoCiPtz
17
+ Mm0NNYkrl2J/brj2jd5WRk6D2BWxf6pQKULzovTCX04pNxfLBBXBlQ==
18
+ -----END RSA PRIVATE KEY-----