xmldsig 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/xmldsig.rb +4 -2
- data/lib/xmldsig/reference.rb +74 -0
- data/lib/xmldsig/signature.rb +13 -49
- data/lib/xmldsig/signed_document.rb +1 -1
- data/lib/xmldsig/version.rb +1 -1
- data/spec/fixtures/unsigned_multiple_references.xml +38 -0
- data/spec/lib/xmldsig/reference_spec.rb +65 -0
- data/spec/lib/xmldsig/signature_spec.rb +19 -33
- metadata +7 -2
data/lib/xmldsig.rb
CHANGED
@@ -8,11 +8,13 @@ require "xmldsig/transforms/transform"
|
|
8
8
|
require "xmldsig/transforms/canonicalize"
|
9
9
|
require "xmldsig/transforms/enveloped_signature"
|
10
10
|
require "xmldsig/transforms"
|
11
|
+
require "xmldsig/reference"
|
11
12
|
require "xmldsig/signature"
|
12
13
|
|
13
14
|
module Xmldsig
|
14
15
|
NAMESPACES = {
|
15
|
-
"ds"
|
16
|
-
"ec"
|
16
|
+
"ds" => "http://www.w3.org/2000/09/xmldsig#",
|
17
|
+
"ec" => "http://www.w3.org/2001/10/xml-exc-c14n#",
|
18
|
+
"wsu" => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
17
19
|
}
|
18
20
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Xmldsig
|
2
|
+
class Reference
|
3
|
+
attr_accessor :reference, :errors
|
4
|
+
|
5
|
+
class ReferencedNodeNotFound < Exception; end
|
6
|
+
|
7
|
+
def initialize(reference)
|
8
|
+
@reference = reference
|
9
|
+
@errors = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def document
|
13
|
+
reference.document
|
14
|
+
end
|
15
|
+
|
16
|
+
def sign
|
17
|
+
self.digest_value = calculate_digest_value
|
18
|
+
end
|
19
|
+
|
20
|
+
def referenced_node
|
21
|
+
if reference_uri && reference_uri != ""
|
22
|
+
id = reference_uri[1..-1]
|
23
|
+
if ref = document.dup.at_xpath("//*[@ID='#{id}' or @wsu:Id='#{id}']", NAMESPACES)
|
24
|
+
ref
|
25
|
+
else
|
26
|
+
raise(
|
27
|
+
ReferencedNodeNotFound,
|
28
|
+
"Could not find the referenced node #{id}'"
|
29
|
+
)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
document.dup.root
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def reference_uri
|
37
|
+
reference.get_attribute("URI")
|
38
|
+
end
|
39
|
+
|
40
|
+
def digest_value
|
41
|
+
Base64.decode64 reference.at_xpath("descendant::ds:DigestValue", NAMESPACES).content
|
42
|
+
end
|
43
|
+
|
44
|
+
def calculate_digest_value
|
45
|
+
node = transforms.apply(referenced_node)
|
46
|
+
digest_method.digest node
|
47
|
+
end
|
48
|
+
|
49
|
+
def digest_method
|
50
|
+
algorithm = reference.at_xpath("descendant::ds:DigestMethod", NAMESPACES).get_attribute("Algorithm")
|
51
|
+
case algorithm
|
52
|
+
when "http://www.w3.org/2001/04/xmlenc#sha256"
|
53
|
+
Digest::SHA2
|
54
|
+
when "http://www.w3.org/2000/09/xmldsig#sha1"
|
55
|
+
Digest::SHA1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def digest_value=(digest_value)
|
60
|
+
reference.at_xpath("descendant::ds:DigestValue", NAMESPACES).content =
|
61
|
+
Base64.encode64(digest_value).chomp
|
62
|
+
end
|
63
|
+
|
64
|
+
def transforms
|
65
|
+
Transforms.new(reference.xpath("descendant::ds:Transform", NAMESPACES))
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_digest_value
|
69
|
+
unless digest_value == calculate_digest_value
|
70
|
+
@errors << :digest_value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/xmldsig/signature.rb
CHANGED
@@ -1,34 +1,23 @@
|
|
1
1
|
module Xmldsig
|
2
2
|
class Signature
|
3
|
-
attr_accessor :signature
|
3
|
+
attr_accessor :signature
|
4
4
|
|
5
5
|
def initialize(signature)
|
6
6
|
@signature = signature
|
7
|
-
@errors = []
|
8
7
|
end
|
9
8
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
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.root
|
9
|
+
def references
|
10
|
+
@references ||= signature.xpath("descendant::ds:Reference", NAMESPACES).map do |node|
|
11
|
+
Reference.new(node)
|
23
12
|
end
|
24
13
|
end
|
25
14
|
|
26
|
-
def
|
27
|
-
|
15
|
+
def errors
|
16
|
+
references.flat_map(&:errors) + @errors
|
28
17
|
end
|
29
18
|
|
30
19
|
def sign(private_key = nil, &block)
|
31
|
-
|
20
|
+
references.each(&:sign)
|
32
21
|
self.signature_value = calculate_signature_value(private_key, &block)
|
33
22
|
end
|
34
23
|
|
@@ -42,18 +31,14 @@ module Xmldsig
|
|
42
31
|
|
43
32
|
def valid?(certificate = nil, &block)
|
44
33
|
@errors = []
|
45
|
-
|
34
|
+
references.each { |r| r.errors = [] }
|
35
|
+
validate_digest_values
|
46
36
|
validate_signature_value(certificate, &block)
|
47
|
-
|
37
|
+
errors.empty?
|
48
38
|
end
|
49
39
|
|
50
40
|
private
|
51
41
|
|
52
|
-
def calculate_digest_value
|
53
|
-
node = transforms.apply(referenced_node)
|
54
|
-
digest_method.digest node
|
55
|
-
end
|
56
|
-
|
57
42
|
def canonicalization_method
|
58
43
|
signed_info.at_xpath("descendant::ds:CanonicalizationMethod", NAMESPACES).get_attribute("Algorithm")
|
59
44
|
end
|
@@ -70,21 +55,6 @@ module Xmldsig
|
|
70
55
|
end
|
71
56
|
end
|
72
57
|
|
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", NAMESPACES).content =
|
85
|
-
Base64.encode64(digest_value).chomp
|
86
|
-
end
|
87
|
-
|
88
58
|
def signature_algorithm
|
89
59
|
signed_info.at_xpath("descendant::ds:SignatureMethod", NAMESPACES).get_attribute("Algorithm")
|
90
60
|
end
|
@@ -104,14 +74,8 @@ module Xmldsig
|
|
104
74
|
Base64.encode64(signature_value).chomp
|
105
75
|
end
|
106
76
|
|
107
|
-
def
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
def validate_digest_value
|
112
|
-
unless digest_value == calculate_digest_value
|
113
|
-
errors << :digest_value
|
114
|
-
end
|
77
|
+
def validate_digest_values
|
78
|
+
references.each(&:validate_digest_value)
|
115
79
|
end
|
116
80
|
|
117
81
|
def validate_signature_value(certificate)
|
@@ -122,7 +86,7 @@ module Xmldsig
|
|
122
86
|
end
|
123
87
|
|
124
88
|
unless signature_valid
|
125
|
-
errors << :signature
|
89
|
+
@errors << :signature
|
126
90
|
end
|
127
91
|
end
|
128
92
|
end
|
data/lib/xmldsig/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
3
|
+
<soapenv:Header></soapenv:Header>
|
4
|
+
<soapenv:Body>
|
5
|
+
|
6
|
+
<SomeMethod ID="Data-1">
|
7
|
+
<Data>some Content</Data>
|
8
|
+
</SomeMethod>
|
9
|
+
|
10
|
+
<Timestamp ID="Timestamp-1">
|
11
|
+
<Created>2010-10-25T12:09:44Z</Created>
|
12
|
+
<Expires>2010-10-25T12:14:44Z</Expires>
|
13
|
+
</Timestamp>
|
14
|
+
|
15
|
+
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
|
16
|
+
<SignedInfo>
|
17
|
+
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
18
|
+
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
|
19
|
+
<Reference URI="#Timestamp-1">
|
20
|
+
<Transforms>
|
21
|
+
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
22
|
+
</Transforms>
|
23
|
+
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
24
|
+
<DigestValue></DigestValue>
|
25
|
+
</Reference>
|
26
|
+
<Reference URI="#Data-1">
|
27
|
+
<Transforms>
|
28
|
+
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
29
|
+
</Transforms>
|
30
|
+
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
31
|
+
<DigestValue></DigestValue>
|
32
|
+
</Reference>
|
33
|
+
</SignedInfo>
|
34
|
+
<SignatureValue></SignatureValue>
|
35
|
+
</Signature>
|
36
|
+
|
37
|
+
</soapenv:Body>
|
38
|
+
</soapenv:Envelope>
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Xmldsig::Reference do
|
4
|
+
let(:document) { Nokogiri::XML::Document.parse File.read("spec/fixtures/signed.xml") }
|
5
|
+
let(:reference) { Xmldsig::Reference.new(document.at_xpath('//ds:Reference', Xmldsig::NAMESPACES)) }
|
6
|
+
|
7
|
+
describe "#digest_value" do
|
8
|
+
it "returns the digest value in the xml" do
|
9
|
+
reference.digest_value.should == Base64.decode64("ftoSYFdze1AWgGHF5N9i9SFKThXkqH2AdyzA3/epbJw=")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#document" do
|
14
|
+
it "returns the document" do
|
15
|
+
reference.document.should == document
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#sign" do
|
20
|
+
let(:document) { Nokogiri::XML::Document.parse File.read("spec/fixtures/unsigned.xml") }
|
21
|
+
|
22
|
+
it "sets the correct digest value" do
|
23
|
+
reference.sign
|
24
|
+
reference.digest_value.should == Base64.decode64("ftoSYFdze1AWgGHF5N9i9SFKThXkqH2AdyzA3/epbJw=")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#referenced_node" do
|
29
|
+
it "returns the referenced_node by id" do
|
30
|
+
reference.referenced_node.to_s.should ==
|
31
|
+
document.at_xpath("//*[@ID='foo']").to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns the referenced node by parent" do
|
35
|
+
reference.stub(:reference_uri).and_return("")
|
36
|
+
reference.referenced_node.to_s.should ==
|
37
|
+
document.root.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns the reference node when using WS-Security style id attribute" do
|
41
|
+
node = document.at_xpath('//*[@ID]')
|
42
|
+
node.add_namespace('wsu', Xmldsig::NAMESPACES['wsu'])
|
43
|
+
node['wsu:Id'] = node['ID']
|
44
|
+
node.remove_attribute('ID')
|
45
|
+
|
46
|
+
reference.referenced_node.
|
47
|
+
attribute_with_ns('Id', Xmldsig::NAMESPACES['wsu']).value.
|
48
|
+
should == 'foo'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises ReferencedNodeNotFound when the refenced node is not present" do
|
52
|
+
node = document.at_xpath('//*[@ID]')
|
53
|
+
node.remove_attribute('ID')
|
54
|
+
|
55
|
+
expect { reference.referenced_node }.
|
56
|
+
to raise_error(Xmldsig::Reference::ReferencedNodeNotFound)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#reference_uri" do
|
61
|
+
it "returns the reference uri" do
|
62
|
+
reference.reference_uri.should == "#foo"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -8,37 +8,6 @@ describe Xmldsig::Signature do
|
|
8
8
|
let(:signature_node) { document.at_xpath("//ds:Signature", Xmldsig::NAMESPACES) }
|
9
9
|
let(:signature) { Xmldsig::Signature.new(signature_node) }
|
10
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.root.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
11
|
describe "#sign" do
|
43
12
|
let(:document) { Nokogiri::XML::Document.parse File.read("spec/fixtures/unsigned.xml") }
|
44
13
|
let(:signature_node) { document.at_xpath("//ds:Signature", Xmldsig::NAMESPACES) }
|
@@ -49,7 +18,7 @@ describe Xmldsig::Signature do
|
|
49
18
|
end
|
50
19
|
|
51
20
|
it "sets the digest value" do
|
52
|
-
signature.digest_value.should == Base64.decode64("ftoSYFdze1AWgGHF5N9i9SFKThXkqH2AdyzA3/epbJw=")
|
21
|
+
signature.references.first.digest_value.should == Base64.decode64("ftoSYFdze1AWgGHF5N9i9SFKThXkqH2AdyzA3/epbJw=")
|
53
22
|
end
|
54
23
|
|
55
24
|
it "sets the signature value" do
|
@@ -72,6 +41,21 @@ describe Xmldsig::Signature do
|
|
72
41
|
")
|
73
42
|
end
|
74
43
|
|
44
|
+
describe "multiple references" do
|
45
|
+
let(:document) { Nokogiri::XML::Document.parse File.read("spec/fixtures/unsigned_multiple_references.xml") }
|
46
|
+
|
47
|
+
it "can sign the document" do
|
48
|
+
signature.sign(private_key)
|
49
|
+
signature.should be_valid(certificate)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "gets a digest per reference" do
|
53
|
+
signature.references.count.should be == 2
|
54
|
+
signature.sign(private_key)
|
55
|
+
signature.references[0].digest_value.should be == Base64.decode64("P1nUq8Y/LPmd+EON/mcNMNRjT78=")
|
56
|
+
signature.references[1].digest_value.should be == Base64.decode64("RoGAaQeuNJuDMWcgsD7RuGbFACo=")
|
57
|
+
end
|
58
|
+
end
|
75
59
|
end
|
76
60
|
|
77
61
|
describe "#signed_info" do
|
@@ -94,7 +78,9 @@ describe Xmldsig::Signature do
|
|
94
78
|
end
|
95
79
|
|
96
80
|
it "returns false if the xml changed" do
|
97
|
-
signature.stub(:document).and_return(
|
81
|
+
signature.references.first.stub(:document).and_return(
|
82
|
+
Nokogiri::XML::Document.parse(File.read("spec/fixtures/signed.xml").gsub("\s\s", "\s"))
|
83
|
+
)
|
98
84
|
signature.valid?(certificate)
|
99
85
|
signature.errors.should include(:digest_value)
|
100
86
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xmldsig
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -44,6 +44,7 @@ files:
|
|
44
44
|
- Rakefile
|
45
45
|
- lib/xmldsig.rb
|
46
46
|
- lib/xmldsig/canonicalizer.rb
|
47
|
+
- lib/xmldsig/reference.rb
|
47
48
|
- lib/xmldsig/signature.rb
|
48
49
|
- lib/xmldsig/signed_document.rb
|
49
50
|
- lib/xmldsig/transforms.rb
|
@@ -63,7 +64,9 @@ files:
|
|
63
64
|
- spec/fixtures/unsigned/with_soap_envelope.xml
|
64
65
|
- spec/fixtures/unsigned/without_namespace_prefix.xml
|
65
66
|
- spec/fixtures/unsigned/without_reference_uri.xml
|
67
|
+
- spec/fixtures/unsigned_multiple_references.xml
|
66
68
|
- spec/fixtures/unsigned_nested_signature.xml
|
69
|
+
- spec/lib/xmldsig/reference_spec.rb
|
67
70
|
- spec/lib/xmldsig/signature_spec.rb
|
68
71
|
- spec/lib/xmldsig/signed_document_spec.rb
|
69
72
|
- spec/lib/xmldsig/transforms/transform_spec.rb
|
@@ -107,7 +110,9 @@ test_files:
|
|
107
110
|
- spec/fixtures/unsigned/with_soap_envelope.xml
|
108
111
|
- spec/fixtures/unsigned/without_namespace_prefix.xml
|
109
112
|
- spec/fixtures/unsigned/without_reference_uri.xml
|
113
|
+
- spec/fixtures/unsigned_multiple_references.xml
|
110
114
|
- spec/fixtures/unsigned_nested_signature.xml
|
115
|
+
- spec/lib/xmldsig/reference_spec.rb
|
111
116
|
- spec/lib/xmldsig/signature_spec.rb
|
112
117
|
- spec/lib/xmldsig/signed_document_spec.rb
|
113
118
|
- spec/lib/xmldsig/transforms/transform_spec.rb
|