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 +7 -0
- data/CHANGELOG.md +48 -0
- data/LICENSE +22 -0
- data/README.md +25 -0
- data/lib/signer/digester.rb +69 -0
- data/lib/signer/version.rb +3 -0
- data/lib/signer.rb +336 -0
- data/spec/fixtures/cert.pem +17 -0
- data/spec/fixtures/input_1.xml +24 -0
- data/spec/fixtures/input_2.xml +7 -0
- data/spec/fixtures/input_3_c14n_comments.xml +22 -0
- data/spec/fixtures/input_4_with_nested_signatures.xml +25 -0
- data/spec/fixtures/key.pem +18 -0
- data/spec/fixtures/output_1.xml +58 -0
- data/spec/fixtures/output_1_inclusive_namespaces.xml +48 -0
- data/spec/fixtures/output_1_sha256.xml +2 -0
- data/spec/fixtures/output_2.xml +31 -0
- data/spec/fixtures/output_2_with_ds_prefix.xml +31 -0
- data/spec/fixtures/output_3_c14n_comments.xml +38 -0
- data/spec/fixtures/output_4_with_nested_signatures.xml +59 -0
- data/spec/fixtures/output_4_with_nested_signatures_with_noblanks_disabled.xml +25 -0
- data/spec/signer_spec.rb +192 -0
- data/spec/spec_helper.rb +5 -0
- metadata +124 -0
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
|
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-----
|