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