xmldsig 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/benoist/xmldsig.png?branch=master)](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
|