xmldsig 0.0.1
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.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yaml +5 -0
- data/Gemfile +12 -0
- data/Guardfile +24 -0
- data/LICENSE +22 -0
- data/README.md +76 -0
- data/Rakefile +10 -0
- data/lib/xmldsig.rb +18 -0
- data/lib/xmldsig/canonicalizer.rb +28 -0
- data/lib/xmldsig/signature.rb +126 -0
- data/lib/xmldsig/signed_document.rb +26 -0
- data/lib/xmldsig/transforms.rb +26 -0
- data/lib/xmldsig/transforms/canonicalize.rb +25 -0
- data/lib/xmldsig/transforms/enveloped_signature.rb +10 -0
- data/lib/xmldsig/transforms/transform.rb +18 -0
- data/lib/xmldsig/version.rb +3 -0
- data/spec/fixtures/certificate.cer +16 -0
- data/spec/fixtures/certificate2.cer +16 -0
- data/spec/fixtures/key.pem +15 -0
- data/spec/fixtures/signed.xml +23 -0
- data/spec/fixtures/unsigned.xml +21 -0
- data/spec/fixtures/unsigned/canonicalizer_1_0.xml +19 -0
- data/spec/fixtures/unsigned/canonicalizer_1_1.xml +19 -0
- data/spec/fixtures/unsigned/canonicalizer_exc.xml +21 -0
- data/spec/fixtures/unsigned/digest_sha1.xml +21 -0
- data/spec/fixtures/unsigned/without_namespace_prefix.xml +19 -0
- data/spec/fixtures/unsigned/without_reference_uri.xml +21 -0
- data/spec/fixtures/unsigned_nested_signature.xml +40 -0
- data/spec/lib/xmldsig/signature_spec.rb +112 -0
- data/spec/lib/xmldsig/signed_document_spec.rb +82 -0
- data/spec/lib/xmldsig/transforms/transform_spec.rb +10 -0
- data/spec/lib/xmldsig_spec.rb +29 -0
- data/spec/spec_helper.rb +22 -0
- data/xmlsec.gemspec +19 -0
- metadata +113 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yaml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec' do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
|
9
|
+
# Rails example
|
10
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
11
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
12
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
13
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
14
|
+
watch('config/routes.rb') { "spec/routing" }
|
15
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
16
|
+
|
17
|
+
# Capybara features specs
|
18
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
19
|
+
|
20
|
+
# Turnip features and steps
|
21
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
22
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
23
|
+
end
|
24
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 benoist
|
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,76 @@
|
|
1
|
+
[](http://travis-ci.org/benoist/xmldsig)
|
2
|
+
# Xmldsig
|
3
|
+
|
4
|
+
This gem is a (partial) implementation of the XMLDsig specification (http://www.w3.org/TR/xmldsig-core)
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'xmldsig'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install xmldsig
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
unsigned_xml = <<-XML
|
24
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
25
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
26
|
+
<foo:Bar>bar</foo:Bar>
|
27
|
+
<ds:Signature>
|
28
|
+
<ds:SignedInfo>
|
29
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
30
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
31
|
+
<ds:Reference URI="#foo">
|
32
|
+
<ds:Transforms>
|
33
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
34
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
|
35
|
+
<ec:InclusiveNamespaces PrefixList="foo"/>
|
36
|
+
</ds:Transform>
|
37
|
+
</ds:Transforms>
|
38
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
39
|
+
<ds:DigestValue></ds:DigestValue>
|
40
|
+
</ds:Reference>
|
41
|
+
</ds:SignedInfo>
|
42
|
+
<ds:SignatureValue></ds:SignatureValue>
|
43
|
+
</ds:Signature>
|
44
|
+
</foo:Foo>
|
45
|
+
XML
|
46
|
+
|
47
|
+
private_key = OpenSSL::PKey::RSA.new(File.read("key.pem"))
|
48
|
+
certificate = OpenSSL::X509::Certificate.new(File.read("certificate.cer"))
|
49
|
+
|
50
|
+
unsigned_document = Xmldsig::SignedDocument.new(unsigned_xml)
|
51
|
+
signed_xml = unsigned_document.sign(private_key)
|
52
|
+
|
53
|
+
# With block
|
54
|
+
signed_xml = unsigned_document.sign do |data|
|
55
|
+
private_key.sign(OpenSSL::Digest::SHA256.new, data)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Validation
|
59
|
+
|
60
|
+
signed_document = Xmldsig::SignedDocument.new(signed_xml)
|
61
|
+
document.verify(certificate)
|
62
|
+
|
63
|
+
# With block
|
64
|
+
signed_document = Xmldsig::SignedDocument.new(signed_xml)
|
65
|
+
document.verify do |signature_value, data|
|
66
|
+
certificate.public_key.verify(OpenSSL::Digest::SHA256.new, signature_value, data)
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
## Contributing
|
71
|
+
|
72
|
+
1. Fork it
|
73
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
74
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
75
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
76
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/xmldsig.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "openssl"
|
3
|
+
require "base64"
|
4
|
+
require "xmldsig/version"
|
5
|
+
require "xmldsig/canonicalizer"
|
6
|
+
require "xmldsig/signed_document"
|
7
|
+
require "xmldsig/transforms/transform"
|
8
|
+
require "xmldsig/transforms/canonicalize"
|
9
|
+
require "xmldsig/transforms/enveloped_signature"
|
10
|
+
require "xmldsig/transforms"
|
11
|
+
require "xmldsig/signature"
|
12
|
+
|
13
|
+
module Xmldsig
|
14
|
+
NAMESPACES = {
|
15
|
+
"ds" => "http://www.w3.org/2000/09/xmldsig#",
|
16
|
+
"ec" => "http://www.w3.org/2001/10/xml-exc-c14n#"
|
17
|
+
}
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Xmldsig
|
2
|
+
class Canonicalizer
|
3
|
+
attr_accessor :node, :method, :inclusive_namespaces
|
4
|
+
|
5
|
+
def initialize(node, method, inclusive_namespaces = [])
|
6
|
+
@node = node
|
7
|
+
@method = method
|
8
|
+
@inclusive_namespaces = inclusive_namespaces
|
9
|
+
end
|
10
|
+
|
11
|
+
def canonicalize
|
12
|
+
node.canonicalize(mode(method), inclusive_namespaces)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def mode(method)
|
18
|
+
case method
|
19
|
+
when "http://www.w3.org/2001/10/xml-exc-c14n#"
|
20
|
+
Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
21
|
+
when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
|
22
|
+
Nokogiri::XML::XML_C14N_1_0
|
23
|
+
when "http://www.w3.org/2006/12/xml-c14n11"
|
24
|
+
Nokogiri::XML::XML_C14N_1_1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Xmldsig
|
2
|
+
class Signature
|
3
|
+
attr_accessor :signature, :errors
|
4
|
+
|
5
|
+
def initialize(signature)
|
6
|
+
@signature = signature
|
7
|
+
@errors = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def digest_value
|
11
|
+
Base64.decode64 signed_info.at_xpath("descendant::ds:DigestValue", NAMESPACES).content
|
12
|
+
end
|
13
|
+
|
14
|
+
def document
|
15
|
+
signature.document
|
16
|
+
end
|
17
|
+
|
18
|
+
def referenced_node
|
19
|
+
if reference_uri && reference_uri != ""
|
20
|
+
document.dup.at_xpath("//*[@ID='#{reference_uri[1..-1]}']")
|
21
|
+
else
|
22
|
+
document.dup.at_xpath(signature.parent.path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def reference_uri
|
27
|
+
signature.at_xpath("descendant::ds:Reference", NAMESPACES).get_attribute("URI")
|
28
|
+
end
|
29
|
+
|
30
|
+
def sign(private_key = nil, &block)
|
31
|
+
self.digest_value = calculate_digest_value
|
32
|
+
self.signature_value = calculate_signature_value(private_key, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def signed_info
|
36
|
+
signature.at_xpath("descendant::ds:SignedInfo", NAMESPACES)
|
37
|
+
end
|
38
|
+
|
39
|
+
def signature_value
|
40
|
+
Base64.decode64 signature.at_xpath("descendant::ds:SignatureValue", NAMESPACES).content
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid?(certificate = nil, &block)
|
44
|
+
@errors = []
|
45
|
+
validate_digest_value
|
46
|
+
validate_signature_value(certificate, &block)
|
47
|
+
@errors.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def calculate_digest_value
|
53
|
+
node = transforms.apply(referenced_node)
|
54
|
+
digest_method.digest node
|
55
|
+
end
|
56
|
+
|
57
|
+
def canonicalization_method
|
58
|
+
signed_info.at_xpath("descendant::ds:CanonicalizationMethod", NAMESPACES).get_attribute("Algorithm")
|
59
|
+
end
|
60
|
+
|
61
|
+
def canonicalized_signed_info
|
62
|
+
Canonicalizer.new(signed_info, canonicalization_method).canonicalize
|
63
|
+
end
|
64
|
+
|
65
|
+
def calculate_signature_value(private_key, &block)
|
66
|
+
if private_key
|
67
|
+
private_key.sign(signature_method.new, canonicalized_signed_info)
|
68
|
+
else
|
69
|
+
yield(canonicalized_signed_info)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def digest_method
|
74
|
+
algorithm = signed_info.at_xpath("descendant::ds:DigestMethod", NAMESPACES).get_attribute("Algorithm")
|
75
|
+
case algorithm
|
76
|
+
when "http://www.w3.org/2001/04/xmlenc#sha256"
|
77
|
+
Digest::SHA2
|
78
|
+
when "http://www.w3.org/2000/09/xmldsig#sha1"
|
79
|
+
Digest::SHA1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def digest_value=(digest_value)
|
84
|
+
signed_info.at_xpath("descendant::ds:DigestValue").content =
|
85
|
+
Base64.encode64(digest_value).chomp
|
86
|
+
end
|
87
|
+
|
88
|
+
def signature_method
|
89
|
+
algorithm = signed_info.at_xpath("descendant::ds:SignatureMethod", NAMESPACES).get_attribute("Algorithm")
|
90
|
+
algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
|
91
|
+
case algorithm
|
92
|
+
when 256 then
|
93
|
+
OpenSSL::Digest::SHA256
|
94
|
+
else
|
95
|
+
OpenSSL::Digest::SHA1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def signature_value=(signature_value)
|
100
|
+
signature.at_xpath("descendant::ds:SignatureValue").content =
|
101
|
+
Base64.encode64(signature_value).chomp
|
102
|
+
end
|
103
|
+
|
104
|
+
def transforms
|
105
|
+
Transforms.new(signature.xpath("descendant::ds:Transform", NAMESPACES))
|
106
|
+
end
|
107
|
+
|
108
|
+
def validate_digest_value
|
109
|
+
unless digest_value == calculate_digest_value
|
110
|
+
errors << :digest_value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def validate_signature_value(certificate)
|
115
|
+
signature_valid = if certificate
|
116
|
+
certificate.public_key.verify(signature_method.new, signature_value, canonicalized_signed_info)
|
117
|
+
else
|
118
|
+
yield(signature_value, canonicalized_signed_info)
|
119
|
+
end
|
120
|
+
|
121
|
+
unless signature_valid
|
122
|
+
errors << :signature
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Xmldsig
|
2
|
+
class SignedDocument
|
3
|
+
attr_accessor :document
|
4
|
+
|
5
|
+
def initialize(document, options = {})
|
6
|
+
@document = Nokogiri::XML::Document.parse(document)
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate(certificate = nil, &block)
|
10
|
+
signatures.all? { |signature| signature.valid?(certificate, &block) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def sign(private_key = nil, &block)
|
14
|
+
signatures.each { |signature| signature.sign(private_key, &block) }
|
15
|
+
@document.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def signed_nodes
|
19
|
+
signatures.collect(&:referenced_node)
|
20
|
+
end
|
21
|
+
|
22
|
+
def signatures
|
23
|
+
document.xpath("//ds:Signature", NAMESPACES).collect { |node| Signature.new(node) } || []
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Xmldsig
|
2
|
+
class Transforms < Array
|
3
|
+
|
4
|
+
def apply(node)
|
5
|
+
@node = node
|
6
|
+
each do |transform_node|
|
7
|
+
@node = get_transform(@node, transform_node).transform
|
8
|
+
end
|
9
|
+
@node
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def get_transform(node, transform_node)
|
15
|
+
case transform_node.get_attribute("Algorithm")
|
16
|
+
when "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
|
17
|
+
Transforms::EnvelopedSignature.new(node, transform_node)
|
18
|
+
when "http://www.w3.org/2001/10/xml-exc-c14n#",
|
19
|
+
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
|
20
|
+
"http://www.w3.org/2006/12/xml-c14n11"
|
21
|
+
Transforms::Canonicalize.new(node, transform_node)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Xmldsig
|
2
|
+
class Transforms < Array
|
3
|
+
class Canonicalize < Transform
|
4
|
+
def transform
|
5
|
+
self.node = Canonicalizer.new(node, algorithm, inclusive_namespaces).canonicalize
|
6
|
+
node
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def algorithm
|
12
|
+
transform_node.get_attribute("Algorithm")
|
13
|
+
end
|
14
|
+
|
15
|
+
def inclusive_namespaces
|
16
|
+
inclusive_namespaces = transform_node.at_xpath("descendant::ec:InclusiveNamespaces", Xmldsig::NAMESPACES)
|
17
|
+
if inclusive_namespaces && inclusive_namespaces.has_attribute?("PrefixList")
|
18
|
+
inclusive_namespaces.get_attribute("PrefixList").to_s.split(" ")
|
19
|
+
else
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Xmldsig
|
2
|
+
class Transforms < Array
|
3
|
+
class Transform
|
4
|
+
|
5
|
+
attr_accessor :node, :transform_node
|
6
|
+
|
7
|
+
def initialize(node, transform_node)
|
8
|
+
@node = node
|
9
|
+
@transform_node = transform_node
|
10
|
+
end
|
11
|
+
|
12
|
+
def transform
|
13
|
+
warn("Transform called but not implemented!")
|
14
|
+
self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIICgjCCAeugAwIBAgIBADANBgkqhkiG9w0BAQUFADA6MQswCQYDVQQGEwJCRTEN
|
3
|
+
MAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVzdDENMAsGA1UEAwwEVGVzdDAeFw0x
|
4
|
+
MzAxMTMxNTMzNDNaFw0xNDAxMTMxNTMzNDNaMDoxCzAJBgNVBAYTAkJFMQ0wCwYD
|
5
|
+
VQQKDARUZXN0MQ0wCwYDVQQLDARUZXN0MQ0wCwYDVQQDDARUZXN0MIGfMA0GCSqG
|
6
|
+
SIb3DQEBAQUAA4GNADCBiQKBgQC37C0mhTmdr8iVfQPQuOKtzG/fhwG4ILuUX1Vk
|
7
|
+
5uN9oSZJxhb5Kn8aBppny1BSekgk12wn4AE/6i7Jfix3SZWoqdaxpdDalvQSdNey
|
8
|
+
n6GmV2oP4lzp6XjXmtRxvOywgTYuhf/DBlpiq7B/vTF7kMwYgs0ahM3mRJG2V7LA
|
9
|
+
RTXUfwIDAQABo4GXMIGUMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBRkMx3Z
|
10
|
+
wHO3Zog0pWdYNB38NRmWMGIGA1UdIwRbMFmAFBRkMx3ZwHO3Zog0pWdYNB38NRmW
|
11
|
+
oT6kPDA6MQswCQYDVQQGEwJCRTENMAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVz
|
12
|
+
dDENMAsGA1UEAwwEVGVzdIIBADANBgkqhkiG9w0BAQUFAAOBgQBs8voSBDgN7HL1
|
13
|
+
i5EP+G/ymWUVenpGvRZCnfkR9Wo4ORzj1Y7ohXHooOzDJ2oi0yDwatXnPpe3hauq
|
14
|
+
QDid6d4i7F1Wpgdo2MibqXP8/DPzhuBARvPSzip+yS6ITjqKN/YN4K+kpja2Sh7D
|
15
|
+
dxWND3opvVHZTXywjZpdF1OsmNhOCg==
|
16
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,16 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIICgjCCAeugAwIBAgIBADANBgkqhkiG9w0BAQUFADA6MQswCQYDVQQGEwJCRTEN
|
3
|
+
MAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVzdDENMAsGA1UEAwwEVGVzdDAeFw0x
|
4
|
+
MzAxMTExNTI4MzdaFw0xNDAxMTExNTI4MzdaMDoxCzAJBgNVBAYTAkJFMQ0wCwYD
|
5
|
+
VQQKDARUZXN0MQ0wCwYDVQQLDARUZXN0MQ0wCwYDVQQDDARUZXN0MIGfMA0GCSqG
|
6
|
+
SIb3DQEBAQUAA4GNADCBiQKBgQDQnLpvMvXoPGcAsdk2j2AeLLPysVtFc1f6CSGA
|
7
|
+
fiIR2dzs8h/MN5R0bFBASDUGUGdYyr0QGcKNtqW/cd3Sr1rS2fh5Bopnq9YS6od6
|
8
|
+
J6P3AGIKwmJuIBwwfsvnX3eKGDYeOqmrIo5mdPRAob2D1+FhXbMxeYbRhGMmItC0
|
9
|
+
dle7LQIDAQABo4GXMIGUMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM7pPtef
|
10
|
+
Oj5Og5wLqI0Lt5UUmw0uMGIGA1UdIwRbMFmAFM7pPtefOj5Og5wLqI0Lt5UUmw0u
|
11
|
+
oT6kPDA6MQswCQYDVQQGEwJCRTENMAsGA1UECgwEVGVzdDENMAsGA1UECwwEVGVz
|
12
|
+
dDENMAsGA1UEAwwEVGVzdIIBADANBgkqhkiG9w0BAQUFAAOBgQBTIt/4/sraPO4g
|
13
|
+
mmY2oSGG19I2Fs24pV/bX8xqI10iexpGsxnpCQIeiDTUHamo12vyXDPx8zANdVTh
|
14
|
+
FSAWHEESBMLrS8pybbAL7sU4ij4JmfxygGk6OEsc3jKY00NYom+Mg3JObIgtjOIK
|
15
|
+
YfrH7uvpm+AIXsef5vrst4MI6GhEAA==
|
16
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,15 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIICXAIBAAKBgQC37C0mhTmdr8iVfQPQuOKtzG/fhwG4ILuUX1Vk5uN9oSZJxhb5
|
3
|
+
Kn8aBppny1BSekgk12wn4AE/6i7Jfix3SZWoqdaxpdDalvQSdNeyn6GmV2oP4lzp
|
4
|
+
6XjXmtRxvOywgTYuhf/DBlpiq7B/vTF7kMwYgs0ahM3mRJG2V7LARTXUfwIDAQAB
|
5
|
+
AoGBAKDQkd3niSw2YiVLTQW4UwNyCLOioT80567g+JKkS28yc374BGhS3xWLhoCQ
|
6
|
+
xieHogMMlRX8iDsxcT1e5FRc88wtIh4vnpUeV++tU9nqpF9SAZV1HAHsOZwyrNUc
|
7
|
+
0nZHgDcyyClirb2wbBG4L+SkrC2kS0MTlx+HiobGpMYsXMh5AkEA4EPrLY7fUaNl
|
8
|
+
bzbJb+Thb3M4UsoXzFuFm0Z/H8ty6aw9yi1pxuh8OiyGGFqPB/gl3zUE1LNdTzx5
|
9
|
+
xLt93Qdf8wJBANHy1EHwTcNbHJ935NQXF8RpllGyZG1X962NwtJ9x46MTPAhOceU
|
10
|
+
9ZU+OCSOvl+7fKjjcw8TsLYiv4q+RMGZKEUCQHPaomOmqzdBceVCKE3lr5AjtbUP
|
11
|
+
Mbwgi6TrhkCmmXadxE3tp/dZotNqrNtn7Pvw9Z+ZhCVdg5arZzx6n0rPxIECQDFZ
|
12
|
+
oBUj1FOgXhkKCKrmBrsvipsHkN22+Mw971alJDxYtFkZpkhItnVvW6kUOKGuI35b
|
13
|
+
gJdBrJ8TieymDulnA/UCQGfl27mqPX4JTzN+RiZHqGyGSzBi1ggH3vsI2oXDQZ7k
|
14
|
+
uOAPiLSBR83eeHGkE2C5fn0QfQGgFNDFjC/6Su/Wvt4=
|
15
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" ID="foo">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
8
|
+
<ds:Reference URI="#foo">
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
|
12
|
+
<ec:InclusiveNamespaces PrefixList="foo"/>
|
13
|
+
</ds:Transform>
|
14
|
+
</ds:Transforms>
|
15
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
16
|
+
<ds:DigestValue>ftoSYFdze1AWgGHF5N9i9SFKThXkqH2AdyzA3/epbJw=</ds:DigestValue>
|
17
|
+
</ds:Reference>
|
18
|
+
</ds:SignedInfo>
|
19
|
+
<ds:SignatureValue>E3yyqsSoxRkhYEuaEtR+SLg85gU5B4a7xUXA+d2Zn6j7F6z73dOd8iYHOusB
|
20
|
+
Ty3C/3ujbmPhHKg8uX9kUE8b+YoOqZt4z9pdxAq44nJEuijwi4doIPpHWirv
|
21
|
+
BnSoP5IoL0DYzGVrgj8udRzfAw5nNeV7wSrBZEn+yrxmUPJoUZc=</ds:SignatureValue>
|
22
|
+
</ds:Signature>
|
23
|
+
</foo:Foo>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
8
|
+
<ds:Reference URI="#foo">
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
|
12
|
+
<ec:InclusiveNamespaces PrefixList="foo"/>
|
13
|
+
</ds:Transform>
|
14
|
+
</ds:Transforms>
|
15
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
16
|
+
<ds:DigestValue></ds:DigestValue>
|
17
|
+
</ds:Reference>
|
18
|
+
</ds:SignedInfo>
|
19
|
+
<ds:SignatureValue></ds:SignatureValue>
|
20
|
+
</ds:Signature>
|
21
|
+
</foo:Foo>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
8
|
+
<ds:Reference URI="#foo">
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
12
|
+
</ds:Transforms>
|
13
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
14
|
+
<ds:DigestValue></ds:DigestValue>
|
15
|
+
</ds:Reference>
|
16
|
+
</ds:SignedInfo>
|
17
|
+
<ds:SignatureValue></ds:SignatureValue>
|
18
|
+
</ds:Signature>
|
19
|
+
</foo:Foo>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
8
|
+
<ds:Reference URI="#foo">
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
12
|
+
</ds:Transforms>
|
13
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
14
|
+
<ds:DigestValue></ds:DigestValue>
|
15
|
+
</ds:Reference>
|
16
|
+
</ds:SignedInfo>
|
17
|
+
<ds:SignatureValue></ds:SignatureValue>
|
18
|
+
</ds:Signature>
|
19
|
+
</foo:Foo>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
8
|
+
<ds:Reference URI="#foo">
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
|
12
|
+
<ec:InclusiveNamespaces PrefixList="foo"/>
|
13
|
+
</ds:Transform>
|
14
|
+
</ds:Transforms>
|
15
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
16
|
+
<ds:DigestValue></ds:DigestValue>
|
17
|
+
</ds:Reference>
|
18
|
+
</ds:SignedInfo>
|
19
|
+
<ds:SignatureValue></ds:SignatureValue>
|
20
|
+
</ds:Signature>
|
21
|
+
</foo:Foo>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
|
8
|
+
<ds:Reference URI="#foo">
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
|
12
|
+
<ec:InclusiveNamespaces PrefixList="foo"/>
|
13
|
+
</ds:Transform>
|
14
|
+
</ds:Transforms>
|
15
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
16
|
+
<ds:DigestValue></ds:DigestValue>
|
17
|
+
</ds:Reference>
|
18
|
+
</ds:SignedInfo>
|
19
|
+
<ds:SignatureValue></ds:SignatureValue>
|
20
|
+
</ds:Signature>
|
21
|
+
</foo:Foo>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
8
|
+
<ds:Reference URI="#foo">
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
12
|
+
</ds:Transforms>
|
13
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
14
|
+
<ds:DigestValue></ds:DigestValue>
|
15
|
+
</ds:Reference>
|
16
|
+
</ds:SignedInfo>
|
17
|
+
<ds:SignatureValue></ds:SignatureValue>
|
18
|
+
</ds:Signature>
|
19
|
+
</foo:Foo>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
8
|
+
<ds:Reference>
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
|
12
|
+
<ec:InclusiveNamespaces PrefixList="foo"/>
|
13
|
+
</ds:Transform>
|
14
|
+
</ds:Transforms>
|
15
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
16
|
+
<ds:DigestValue></ds:DigestValue>
|
17
|
+
</ds:Reference>
|
18
|
+
</ds:SignedInfo>
|
19
|
+
<ds:SignatureValue></ds:SignatureValue>
|
20
|
+
</ds:Signature>
|
21
|
+
</foo:Foo>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<foo:Foo ID="foo" xmlns:foo="http://example.com/foo#" xmlns:baz="http://example.com/baz#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#">
|
3
|
+
<foo:Bar>bar</foo:Bar>
|
4
|
+
<ds:Signature>
|
5
|
+
<ds:SignedInfo>
|
6
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
7
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
8
|
+
<ds:Reference URI="#foo">
|
9
|
+
<ds:Transforms>
|
10
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
11
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
|
12
|
+
<ec:InclusiveNamespaces PrefixList="foo baz"/>
|
13
|
+
</ds:Transform>
|
14
|
+
</ds:Transforms>
|
15
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
16
|
+
<ds:DigestValue></ds:DigestValue>
|
17
|
+
</ds:Reference>
|
18
|
+
</ds:SignedInfo>
|
19
|
+
<ds:SignatureValue></ds:SignatureValue>
|
20
|
+
</ds:Signature>
|
21
|
+
<baz:Baz ID="baz">
|
22
|
+
<ds:Signature>
|
23
|
+
<ds:SignedInfo>
|
24
|
+
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
25
|
+
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
26
|
+
<ds:Reference URI="#baz">
|
27
|
+
<ds:Transforms>
|
28
|
+
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
29
|
+
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
|
30
|
+
<ec:InclusiveNamespaces PrefixList="foo baz"/>
|
31
|
+
</ds:Transform>
|
32
|
+
</ds:Transforms>
|
33
|
+
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
34
|
+
<ds:DigestValue></ds:DigestValue>
|
35
|
+
</ds:Reference>
|
36
|
+
</ds:SignedInfo>
|
37
|
+
<ds:SignatureValue></ds:SignatureValue>
|
38
|
+
</ds:Signature>
|
39
|
+
</baz:Baz>
|
40
|
+
</foo:Foo>
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Xmldsig::Signature do
|
4
|
+
let(:certificate) { OpenSSL::X509::Certificate.new(File.read("spec/fixtures/certificate.cer")) }
|
5
|
+
let(:other_certificate) { OpenSSL::X509::Certificate.new(File.read("spec/fixtures/certificate2.cer")) }
|
6
|
+
let(:private_key) { OpenSSL::PKey::RSA.new(File.read("spec/fixtures/key.pem")) }
|
7
|
+
let(:document) { Nokogiri::XML::Document.parse File.read("spec/fixtures/signed.xml") }
|
8
|
+
let(:signature_node) { document.at_xpath("//ds:Signature", Xmldsig::NAMESPACES) }
|
9
|
+
let(:signature) { Xmldsig::Signature.new(signature_node) }
|
10
|
+
|
11
|
+
describe "#digest_value" do
|
12
|
+
it "returns the digest value in the xml" do
|
13
|
+
signature.digest_value.should == Base64.decode64("ftoSYFdze1AWgGHF5N9i9SFKThXkqH2AdyzA3/epbJw=")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#document" do
|
18
|
+
it "returns the document" do
|
19
|
+
signature.document.should == document
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#referenced_node" do
|
24
|
+
it "returns the referenced_node by id" do
|
25
|
+
signature.referenced_node.to_s.should ==
|
26
|
+
document.at_xpath("//*[@ID='foo']").to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns the referenced node by parent" do
|
30
|
+
signature.stub(:reference_uri).and_return("")
|
31
|
+
signature.referenced_node.to_s.should ==
|
32
|
+
document.at_xpath("//*[@ID='foo']").to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#reference_uri" do
|
37
|
+
it "returns the reference uri" do
|
38
|
+
signature.reference_uri.should == "#foo"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#sign" do
|
43
|
+
let(:document) { Nokogiri::XML::Document.parse File.read("spec/fixtures/unsigned.xml") }
|
44
|
+
let(:signature_node) { document.at_xpath("//ds:Signature", Xmldsig::NAMESPACES) }
|
45
|
+
let(:signature) { Xmldsig::Signature.new(signature_node) }
|
46
|
+
|
47
|
+
before :each do
|
48
|
+
signature.sign(private_key)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "sets the digest value" do
|
52
|
+
signature.digest_value.should == Base64.decode64("ftoSYFdze1AWgGHF5N9i9SFKThXkqH2AdyzA3/epbJw=")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "sets the signature value" do
|
56
|
+
signature.signature_value.should == Base64.decode64("
|
57
|
+
E3yyqsSoxRkhYEuaEtR+SLg85gU5B4a7xUXA+d2Zn6j7F6z73dOd8iYHOusB
|
58
|
+
Ty3C/3ujbmPhHKg8uX9kUE8b+YoOqZt4z9pdxAq44nJEuijwi4doIPpHWirv
|
59
|
+
BnSoP5IoL0DYzGVrgj8udRzfAw5nNeV7wSrBZEn+yrxmUPJoUZc=
|
60
|
+
")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "accepts a block" do
|
64
|
+
signature.sign do |data|
|
65
|
+
private_key.sign(OpenSSL::Digest::SHA256.new, data)
|
66
|
+
end
|
67
|
+
signature.signature_value.should == Base64.decode64("
|
68
|
+
E3yyqsSoxRkhYEuaEtR+SLg85gU5B4a7xUXA+d2Zn6j7F6z73dOd8iYHOusB
|
69
|
+
Ty3C/3ujbmPhHKg8uX9kUE8b+YoOqZt4z9pdxAq44nJEuijwi4doIPpHWirv
|
70
|
+
BnSoP5IoL0DYzGVrgj8udRzfAw5nNeV7wSrBZEn+yrxmUPJoUZc=
|
71
|
+
")
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#signed_info" do
|
77
|
+
it "returns the canonicalized signed info element" do
|
78
|
+
signature.signed_info.to_s.should ==
|
79
|
+
document.at_xpath("//ds:SignedInfo", Xmldsig::NAMESPACES).to_s
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#signature_value" do
|
84
|
+
it "returns the signature value" do
|
85
|
+
signature.signature_value.should ==
|
86
|
+
Base64.decode64(document.at_xpath("//ds:SignatureValue", Xmldsig::NAMESPACES).content)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#valid?" do
|
91
|
+
it "returns true with the correct certificate" do
|
92
|
+
signature.valid?(certificate).should be_true
|
93
|
+
end
|
94
|
+
|
95
|
+
it "returns false if the xml changed" do
|
96
|
+
signature.stub(:document).and_return(Nokogiri::XML::Document.parse(File.read("spec/fixtures/signed.xml").gsub("\s\s", "\s")))
|
97
|
+
signature.valid?(certificate)
|
98
|
+
signature.errors.should include(:digest_value)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "returns false with a difference certificate" do
|
102
|
+
signature.valid?(other_certificate).should be_false
|
103
|
+
end
|
104
|
+
|
105
|
+
it "accepts a block" do
|
106
|
+
signature.valid? do |signature_value, data|
|
107
|
+
certificate.public_key.verify(OpenSSL::Digest::SHA256.new, signature_value, data)
|
108
|
+
end
|
109
|
+
signature.errors.should be_empty
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Xmldsig::SignedDocument do
|
4
|
+
let(:signed_xml) { File.read("spec/fixtures/signed.xml") }
|
5
|
+
let(:signed_document) { Xmldsig::SignedDocument.new(signed_xml) }
|
6
|
+
let(:unsigned_xml) { File.read("spec/fixtures/unsigned.xml") }
|
7
|
+
let(:unsigned_document) { Xmldsig::SignedDocument.new(unsigned_xml) }
|
8
|
+
let(:private_key) { OpenSSL::PKey::RSA.new(File.read("spec/fixtures/key.pem")) }
|
9
|
+
let(:certificate) { OpenSSL::X509::Certificate.new(File.read("spec/fixtures/certificate.cer")) }
|
10
|
+
let(:other_certificate) { OpenSSL::X509::Certificate.new(File.read("spec/fixtures/certificate2.cer")) }
|
11
|
+
|
12
|
+
describe "#initialize" do
|
13
|
+
it "sets the document to a nokogiri document" do
|
14
|
+
document = described_class.new(signed_xml)
|
15
|
+
document.document.should be_a(Nokogiri::XML::Document)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#signatures" do
|
20
|
+
it "returns only the signed nodes" do
|
21
|
+
signed_document.signatures.should be_all { |signature| signature.is_a?(Xmldsig::Signature) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#signed_nodes" do
|
26
|
+
it "returns only the signed nodes" do
|
27
|
+
signed_document.signed_nodes.collect(&:name).should == %w(Foo)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#validate" do
|
32
|
+
it "returns true if the signature and digest value are correct" do
|
33
|
+
signed_document.validate(certificate).should be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns false if the certificate is not valid" do
|
37
|
+
signed_document.validate(other_certificate).should be_false
|
38
|
+
end
|
39
|
+
|
40
|
+
it "accepts a block" do
|
41
|
+
signed_document.validate do |signature_value, data|
|
42
|
+
certificate.public_key.verify(OpenSSL::Digest::SHA256.new, signature_value, data)
|
43
|
+
end.should be_true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#sign" do
|
48
|
+
it "returns a signed document" do
|
49
|
+
signed_document = unsigned_document.sign(private_key)
|
50
|
+
Xmldsig::SignedDocument.new(signed_document).validate(certificate).should be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
it "accepts a block" do
|
54
|
+
signed_document = unsigned_document.sign do |data|
|
55
|
+
private_key.sign(OpenSSL::Digest::SHA256.new, data)
|
56
|
+
end
|
57
|
+
Xmldsig::SignedDocument.new(signed_document).validate(certificate).should be_true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
describe "Nested Signatures" do
|
63
|
+
let(:unsigned_xml) { File.read("spec/fixtures/unsigned_nested_signature.xml") }
|
64
|
+
let(:unsigned_document) { Xmldsig::SignedDocument.new(unsigned_xml) }
|
65
|
+
let(:signed_document) { unsigned_document.sign(private_key) }
|
66
|
+
|
67
|
+
it "when signed should be valid" do
|
68
|
+
Xmldsig::SignedDocument.new(signed_document).validate(certificate).should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should sign 2 elements" do
|
72
|
+
unsigned_document.signed_nodes.count.should == 2
|
73
|
+
end
|
74
|
+
|
75
|
+
it "allows individual signs" do
|
76
|
+
unsigned_document.signatures.last.sign(private_key)
|
77
|
+
unsigned_document.validate(certificate).should be_false
|
78
|
+
unsigned_document.signatures.last.valid?(certificate).should be_true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Xmldsig do
|
4
|
+
let(:private_key) { OpenSSL::PKey::RSA.new(File.read("spec/fixtures/key.pem")) }
|
5
|
+
let(:certificate) { OpenSSL::X509::Certificate.new(File.read("spec/fixtures/certificate.cer")) }
|
6
|
+
|
7
|
+
describe "Sign unsigned documents" do
|
8
|
+
|
9
|
+
%w(
|
10
|
+
canonicalizer_1_0
|
11
|
+
canonicalizer_1_1
|
12
|
+
canonicalizer_exc
|
13
|
+
digest_sha1
|
14
|
+
without_namespace_prefix
|
15
|
+
without_reference_uri
|
16
|
+
).each do |document|
|
17
|
+
describe "#{document}" do
|
18
|
+
let(:unsigned_xml) { File.read("spec/fixtures/unsigned/#{document}.xml") }
|
19
|
+
let(:unsigned_document) { Xmldsig::SignedDocument.new(unsigned_xml) }
|
20
|
+
|
21
|
+
it "should be signable an validateable" do
|
22
|
+
signed_document = unsigned_document.sign(private_key)
|
23
|
+
Xmldsig::SignedDocument.new(signed_document).validate(certificate).should be_true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
4
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
5
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
6
|
+
# loaded once.
|
7
|
+
#
|
8
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
9
|
+
|
10
|
+
require 'xmldsig'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.filter_run :focus
|
16
|
+
|
17
|
+
# Run specs in random order to surface order dependencies. If you find an
|
18
|
+
# order dependency and want to debug it, you can fix the order by providing
|
19
|
+
# the seed, which is printed after each run.
|
20
|
+
# --seed 1234
|
21
|
+
config.order = 'random'
|
22
|
+
end
|
data/xmlsec.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/xmldsig/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["benoist"]
|
6
|
+
gem.email = ["benoist.claassen@gmail.com"]
|
7
|
+
gem.description = %q{This gem is a (partial) implementation of the XMLDsig specification}
|
8
|
+
gem.summary = %q{This gem is a (partial) implementation of the XMLDsig specification (http://www.w3.org/TR/xmldsig-core)}
|
9
|
+
gem.homepage = "https://github.com/benoist/xmldsig"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "xmldsig"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Xmldsig::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency("nokogiri")
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xmldsig
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- benoist
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: This gem is a (partial) implementation of the XMLDsig specification
|
31
|
+
email:
|
32
|
+
- benoist.claassen@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- .rspec
|
39
|
+
- .travis.yaml
|
40
|
+
- Gemfile
|
41
|
+
- Guardfile
|
42
|
+
- LICENSE
|
43
|
+
- README.md
|
44
|
+
- Rakefile
|
45
|
+
- lib/xmldsig.rb
|
46
|
+
- lib/xmldsig/canonicalizer.rb
|
47
|
+
- lib/xmldsig/signature.rb
|
48
|
+
- lib/xmldsig/signed_document.rb
|
49
|
+
- lib/xmldsig/transforms.rb
|
50
|
+
- lib/xmldsig/transforms/canonicalize.rb
|
51
|
+
- lib/xmldsig/transforms/enveloped_signature.rb
|
52
|
+
- lib/xmldsig/transforms/transform.rb
|
53
|
+
- lib/xmldsig/version.rb
|
54
|
+
- spec/fixtures/certificate.cer
|
55
|
+
- spec/fixtures/certificate2.cer
|
56
|
+
- spec/fixtures/key.pem
|
57
|
+
- spec/fixtures/signed.xml
|
58
|
+
- spec/fixtures/unsigned.xml
|
59
|
+
- spec/fixtures/unsigned/canonicalizer_1_0.xml
|
60
|
+
- spec/fixtures/unsigned/canonicalizer_1_1.xml
|
61
|
+
- spec/fixtures/unsigned/canonicalizer_exc.xml
|
62
|
+
- spec/fixtures/unsigned/digest_sha1.xml
|
63
|
+
- spec/fixtures/unsigned/without_namespace_prefix.xml
|
64
|
+
- spec/fixtures/unsigned/without_reference_uri.xml
|
65
|
+
- spec/fixtures/unsigned_nested_signature.xml
|
66
|
+
- spec/lib/xmldsig/signature_spec.rb
|
67
|
+
- spec/lib/xmldsig/signed_document_spec.rb
|
68
|
+
- spec/lib/xmldsig/transforms/transform_spec.rb
|
69
|
+
- spec/lib/xmldsig_spec.rb
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- xmlsec.gemspec
|
72
|
+
homepage: https://github.com/benoist/xmldsig
|
73
|
+
licenses: []
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.8.24
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: This gem is a (partial) implementation of the XMLDsig specification (http://www.w3.org/TR/xmldsig-core)
|
96
|
+
test_files:
|
97
|
+
- spec/fixtures/certificate.cer
|
98
|
+
- spec/fixtures/certificate2.cer
|
99
|
+
- spec/fixtures/key.pem
|
100
|
+
- spec/fixtures/signed.xml
|
101
|
+
- spec/fixtures/unsigned.xml
|
102
|
+
- spec/fixtures/unsigned/canonicalizer_1_0.xml
|
103
|
+
- spec/fixtures/unsigned/canonicalizer_1_1.xml
|
104
|
+
- spec/fixtures/unsigned/canonicalizer_exc.xml
|
105
|
+
- spec/fixtures/unsigned/digest_sha1.xml
|
106
|
+
- spec/fixtures/unsigned/without_namespace_prefix.xml
|
107
|
+
- spec/fixtures/unsigned/without_reference_uri.xml
|
108
|
+
- spec/fixtures/unsigned_nested_signature.xml
|
109
|
+
- spec/lib/xmldsig/signature_spec.rb
|
110
|
+
- spec/lib/xmldsig/signed_document_spec.rb
|
111
|
+
- spec/lib/xmldsig/transforms/transform_spec.rb
|
112
|
+
- spec/lib/xmldsig_spec.rb
|
113
|
+
- spec/spec_helper.rb
|