eet_signer 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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-----