line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package XML::Sig::OO; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION="0.008"; |
4
|
|
|
|
|
|
|
|
5
|
1
|
|
|
1
|
|
1071
|
use Modern::Perl; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
8
|
|
6
|
1
|
|
|
1
|
|
819
|
use Moo; |
|
1
|
|
|
|
|
7383
|
|
|
1
|
|
|
|
|
5
|
|
7
|
1
|
|
|
1
|
|
2079
|
use MooX::Types::MooseLike::Base qw(:all); |
|
1
|
|
|
|
|
9131
|
|
|
1
|
|
|
|
|
339
|
|
8
|
1
|
|
|
1
|
|
8
|
use MIME::Base64; |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
59
|
|
9
|
1
|
|
|
1
|
|
737
|
use XML::LibXML; |
|
1
|
|
|
|
|
42791
|
|
|
1
|
|
|
|
|
6
|
|
10
|
1
|
|
|
1
|
|
159
|
use XML::LibXML::XPathContext; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
21
|
|
11
|
1
|
|
|
1
|
|
529
|
use Crypt::OpenSSL::X509; |
|
1
|
|
|
|
|
13585
|
|
|
1
|
|
|
|
|
162
|
|
12
|
1
|
|
|
1
|
|
698
|
use Crypt::OpenSSL::RSA; |
|
1
|
|
|
|
|
5324
|
|
|
1
|
|
|
|
|
72
|
|
13
|
1
|
|
|
1
|
|
12
|
use Crypt::OpenSSL::Bignum; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
26
|
|
14
|
1
|
|
|
1
|
|
526
|
use Crypt::OpenSSL::DSA; |
|
1
|
|
|
|
|
876
|
|
|
1
|
|
|
|
|
52
|
|
15
|
1
|
|
|
1
|
|
487
|
use Crypt::OpenSSL::VerifyX509; |
|
1
|
|
|
|
|
697
|
|
|
1
|
|
|
|
|
38
|
|
16
|
1
|
|
|
1
|
|
990
|
use Digest::SHA qw(sha1); |
|
1
|
|
|
|
|
3576
|
|
|
1
|
|
|
|
|
122
|
|
17
|
1
|
|
|
1
|
|
585
|
use Ref::Util qw( is_plain_hashref); |
|
1
|
|
|
|
|
1826
|
|
|
1
|
|
|
|
|
81
|
|
18
|
1
|
|
|
1
|
|
501
|
use Data::Result; |
|
1
|
|
|
|
|
56506
|
|
|
1
|
|
|
|
|
53
|
|
19
|
1
|
|
|
1
|
|
12
|
use Carp qw(croak); |
|
1
|
|
|
|
|
17
|
|
|
1
|
|
|
|
|
77
|
|
20
|
1
|
|
|
1
|
|
7
|
use Scalar::Util qw(looks_like_number); |
|
1
|
|
|
|
|
22
|
|
|
1
|
|
|
|
|
55
|
|
21
|
1
|
|
|
1
|
|
6
|
use namespace::clean; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
9
|
|
22
|
1
|
|
|
1
|
|
1345
|
use constant TRANSFORM_EXC_C14N => 'http://www.w3.org/2001/10/xml-exc-c14n#'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
93
|
|
23
|
1
|
|
|
1
|
|
7
|
use constant TRANSFORM_EXC_C14N_COMMENTS => 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
9157
|
|
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 NAME |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
XML::Sig::OO - Modern XML Signatured validation |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 SYNOPSIS |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
use XML::Sig::OO; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Sign our xml |
34
|
|
|
|
|
|
|
my $s=new XML::Sig::OO( |
35
|
|
|
|
|
|
|
xml=>'<?xml version="1.0" standalone="yes"?><data><test ID="A" /><test ID="B" /></data>', |
36
|
|
|
|
|
|
|
key_file=>'rsa_key.pem' |
37
|
|
|
|
|
|
|
cert_file=>'cert.pem', |
38
|
|
|
|
|
|
|
); |
39
|
|
|
|
|
|
|
my $result=$s->sign; |
40
|
|
|
|
|
|
|
die "Failed to sign the xml, error was: $result" unless $result; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
my $xml=$result->get_data; |
43
|
|
|
|
|
|
|
# Example checking a signature |
44
|
|
|
|
|
|
|
my $v=new XML::Sig::OO(xml=>$xml); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# validate our xml |
47
|
|
|
|
|
|
|
my $result=$v->validate; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
if($result) { |
50
|
|
|
|
|
|
|
print "everything checks out!\n"; |
51
|
|
|
|
|
|
|
} else { |
52
|
|
|
|
|
|
|
foreach my $chunk (@{$result->get_data}) { |
53
|
|
|
|
|
|
|
my ($nth,$signature,$digest)=@{$chunk}{qw(nth signature digest)}; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
print "Results for processing chunk $nth\n"; |
56
|
|
|
|
|
|
|
print "Signature State: ".($signature ? "OK\n" : "Failed, error was $signature\n"); |
57
|
|
|
|
|
|
|
print "Digest State: ".($digest ? "OK\n" : "Failed, error was $digest\n"); |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 DESCRIPTION |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
L<XML::Sig::OO> is a project to create a stand alone perl module that does a good job creating and validating xml signatures. At its core This module is written around libxml2 better known as L<XML::LibXML>. |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head1 Multiple signatures and keys |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
In the case of signing multiple //@ID elements, it is possible to sign each chunk with a different key, in fact you can even use completly different key types. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
use Modern::Perl; |
70
|
|
|
|
|
|
|
use XML::Sig::OO; |
71
|
|
|
|
|
|
|
use File::Spec; |
72
|
|
|
|
|
|
|
use FindBin qw($Bin); |
73
|
|
|
|
|
|
|
use Crypt::OpenSSL::DSA; |
74
|
|
|
|
|
|
|
use Crypt::OpenSSL::RSA; |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# create our signign object |
77
|
|
|
|
|
|
|
my $s=new XML::Sig::OO( |
78
|
|
|
|
|
|
|
xml=>'<?xml version="1.0" standalone="yes"?><data><test ID="A" /><test ID="B" /></data>', |
79
|
|
|
|
|
|
|
); |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
my $x=$s->build_xpath; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# sign our first xml chunk with our rsa key! |
84
|
|
|
|
|
|
|
my $rsa_str=join '',IO::File->new(File::Spec->catfile($Bin,'x509_key.pem'))->getlines; |
85
|
|
|
|
|
|
|
my $rsa=Crypt::OpenSSL::RSA->new_private_key($rsa_str); |
86
|
|
|
|
|
|
|
$rsa->use_pkcs1_padding(); |
87
|
|
|
|
|
|
|
my $cert_str=join '',IO::File->new(File::Spec->catfile($Bin,'x509_cert.pem'))->getlines; |
88
|
|
|
|
|
|
|
$s->sign_cert($rsa); |
89
|
|
|
|
|
|
|
$s->key_type('rsa'); |
90
|
|
|
|
|
|
|
$s->cert_string($cert_str); |
91
|
|
|
|
|
|
|
my $result=$s->sign_chunk($x,1); |
92
|
|
|
|
|
|
|
die $result unless $result; |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# Sign our 2nd chunk with our dsa key |
95
|
|
|
|
|
|
|
my $dsa = Crypt::OpenSSL::DSA->read_priv_key(File::Spec->catfile($Bin,'dsa_priv.pem')); |
96
|
|
|
|
|
|
|
$s->cert_string(undef); |
97
|
|
|
|
|
|
|
$s->sign_cert($dsa); |
98
|
|
|
|
|
|
|
$s->key_type('dsa'); |
99
|
|
|
|
|
|
|
$result=$s->sign_chunk($x,2); |
100
|
|
|
|
|
|
|
die $result unless $result; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my ($node)=$x->findnodes($s->xpath_Root); |
103
|
|
|
|
|
|
|
my $xml=$node->toString; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
print "Our Signed XML IS: \n",$xml,"\n"; |
106
|
|
|
|
|
|
|
# Example checking a signature |
107
|
|
|
|
|
|
|
my $v=new XML::Sig::OO(xml=>$xml); |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
$result=$v->validate; |
110
|
|
|
|
|
|
|
die $result unless $result; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
print "Our signed and xml passes validation\n"; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head2 Working with Net::SAML2 |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
L<Net::SAML2> has many problems when it comes to signature validation of xml strings. This section documents how to use this module in place of the Net::SAML2 built ins. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
use Net::SAML2::Protocol::Assertion; |
119
|
|
|
|
|
|
|
use XML::Sig::OO; |
120
|
|
|
|
|
|
|
use MIME::Base64; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# Lets assume we have a post binding response |
123
|
|
|
|
|
|
|
my $saml_response=..... |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
my $xml=decode_base64($saml_response); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
my $v=XML::Sig::OO->new(xml=>$xml,cacert=>'idp_cert.pem'); |
128
|
|
|
|
|
|
|
my $result=$v->validate; |
129
|
|
|
|
|
|
|
die $result unless $result; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# we can now use the asertion knowing it was from our idp |
132
|
|
|
|
|
|
|
my $assertion=Net::SAML2::Protocol::Assertion->new_from_xml(xml=>$xml) |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head2 Encrypted keys |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Although this package does not directly support encrypted keys, it is possible to use encrypted keys by loading and exporting them with the L<Crypt::PK::RSA> and L<Crypt::PK::DSA> packages. |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=head1 Constructor options |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=cut |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=over 4 |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=item * xml=>'...' |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
The base xml string to validate or sign. This option is always required. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=cut |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
has xml=>( |
151
|
|
|
|
|
|
|
is=>'ro', |
152
|
|
|
|
|
|
|
isa=>Str, |
153
|
|
|
|
|
|
|
required=>1, |
154
|
|
|
|
|
|
|
); |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=item * cacert=>'/path/to/your/cacert.pem' |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Optional, used to validate X509 certs. |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=cut |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
has cacert=>( |
163
|
|
|
|
|
|
|
is=>'ro', |
164
|
|
|
|
|
|
|
isa=>sub { my ($f)=@_; croak "cacert must be a readable file" unless defined($f) && -r $f }, |
165
|
|
|
|
|
|
|
required=>0, |
166
|
|
|
|
|
|
|
); |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=item * build_parser=>sub { return XML::LibXML->new() } |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Callback that returns a new XML Parser |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=cut |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
has build_parser=>( |
175
|
|
|
|
|
|
|
is=>'ro', |
176
|
|
|
|
|
|
|
isa=>CodeRef, |
177
|
|
|
|
|
|
|
default=>sub { sub { XML::LibXML->new() } }, |
178
|
|
|
|
|
|
|
); |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=item * namespaces=>{ ds=>'http://www.w3.org/2000/09/xmldsig#', ec=>'http://www.w3.org/2001/10/xml-exc-c14n#'} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
Contains the list of namespaces to set in our XML::LibXML::XPathContext object. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=cut |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
has namespaces=>( |
187
|
|
|
|
|
|
|
is=>'ro', |
188
|
|
|
|
|
|
|
isa=>HashRef, |
189
|
|
|
|
|
|
|
default=>sub { |
190
|
|
|
|
|
|
|
{ |
191
|
|
|
|
|
|
|
ds=>'http://www.w3.org/2000/09/xmldsig#', |
192
|
|
|
|
|
|
|
ec=>'http://www.w3.org/2001/10/xml-exc-c14n#', |
193
|
|
|
|
|
|
|
samlp=>"urn:oasis:names:tc:SAML:2.0:protocol", |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
}, |
196
|
|
|
|
|
|
|
); |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item * digest_cbs=>{ ... } |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Contains the digest callbacks. The default handlers can be found in %XML::SIG::OO::DIGEST. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=cut |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
our %DIGEST=( |
205
|
|
|
|
|
|
|
'http://www.w3.org/2000/09/xmldsig#sha1' => sub { my ($self,$content)=@_; $self->_get_digest(sha1 => $content) }, |
206
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmlenc#sha256' => sub { my ($self,$content)=@_; $self->_get_digest(sha256 => $content) }, |
207
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmlenc#sha512' => sub { my ($self,$content)=@_; $self->_get_digest(sha512 => $content) }, |
208
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#sha224' => sub { my ($self,$content)=@_; $self->_get_digest(sha224 => $content) }, |
209
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#sha384' => sub { my ($self,$content)=@_; $self->_get_digest(sha384 => $content) }, |
210
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#sha512' => sub { my ($self,$content)=@_; $self->_get_digest(sha512 => $content) }, |
211
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#sha1024' => sub { my ($self,$content)=@_; $self->_get_digest(sha1024 => $content) }, |
212
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#sha2048' => sub { my ($self,$content)=@_; $self->_get_digest(sha2048=> $content) }, |
213
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#sha3072' => sub { my ($self,$content)=@_; $self->_get_digest(sha3072=> $content) }, |
214
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#sha4096' => sub { my ($self,$content)=@_; $self->_get_digest(sha4096=> $content) }, |
215
|
|
|
|
|
|
|
); |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=item * digest_method=>'http://www.w3.org/2000/09/xmldsig#sha1' |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
Sets the digest method to be used when signing xml |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=cut |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
has digest_method=>( |
224
|
|
|
|
|
|
|
isa=>sub { exists $DIGEST{$_[0]} or croak "$_[0] is not a supported digest" }, |
225
|
|
|
|
|
|
|
is=>'ro', |
226
|
|
|
|
|
|
|
default=>'http://www.w3.org/2000/09/xmldsig#sha1', |
227
|
|
|
|
|
|
|
); |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=item * key_type=>'rsa' |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
The signature method we will use |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=cut |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
has key_type=>( |
236
|
|
|
|
|
|
|
isa=>sub { croak "unsuported key type: $_[0]" unless $_[0]=~ /^(?:dsa|rsa|x509)$/s }, |
237
|
|
|
|
|
|
|
is=>'rw', |
238
|
|
|
|
|
|
|
required=>0, |
239
|
|
|
|
|
|
|
lazy=>1, |
240
|
|
|
|
|
|
|
default=>'x509', |
241
|
|
|
|
|
|
|
); |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
has digest_cbs=>( |
244
|
|
|
|
|
|
|
isa=>HashRef, |
245
|
|
|
|
|
|
|
is=>'ro', |
246
|
|
|
|
|
|
|
default=>sub { return { %DIGEST} }, |
247
|
|
|
|
|
|
|
); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
sub _get_digest { |
250
|
28
|
|
|
28
|
|
100
|
my ($self,$algo, $content) = @_; |
251
|
28
|
|
|
|
|
1042
|
my $digest = Digest::SHA->can("${algo}_base64")->($content); |
252
|
28
|
|
|
|
|
121
|
while (length($digest) % 4) { $digest .= '=' } |
|
28
|
|
|
|
|
110
|
|
253
|
28
|
|
|
|
|
102
|
return $digest; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
our %TUNE_CERT=( |
257
|
|
|
|
|
|
|
'http://www.w3.org/2000/09/xmldsig#dsa-sha1' => sub { _tune_cert(@_,'sha1') }, |
258
|
|
|
|
|
|
|
'http://www.w3.org/2000/09/xmldsig#rsa-sha1' => sub { _tune_cert(@_,'sha1') }, |
259
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha224' => sub { _tune_cert(@_,'sha224') }, |
260
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' => sub { _tune_cert(@_,'sha256') }, |
261
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384' => sub { _tune_cert(@_,'sha384') }, |
262
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512' => sub { _tune_cert(@_,'sha512') }, |
263
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha1024' => sub { _tune_cert(@_,'sha1024') }, |
264
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha2048' => sub { _tune_cert(@_,'sha2048') }, |
265
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha3072' => sub { _tune_cert(@_,'sha3072') }, |
266
|
|
|
|
|
|
|
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha4096' => sub { _tune_cert(@_,'sha4096') }, |
267
|
|
|
|
|
|
|
); |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=item * signature_method=>'http://www.w3.org/2000/09/xmldsig#rsa-sha1' |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
Sets the signature method. |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=cut |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
has signature_method=>( |
276
|
|
|
|
|
|
|
isa=>Str, |
277
|
|
|
|
|
|
|
is=>'ro', |
278
|
|
|
|
|
|
|
default=>'http://www.w3.org/2000/09/xmldsig#rsa-sha1', |
279
|
|
|
|
|
|
|
); |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub _tune_cert { |
282
|
27
|
|
|
27
|
|
72
|
my ($self,$cert,$alg)=@_; |
283
|
|
|
|
|
|
|
|
284
|
27
|
|
|
|
|
91
|
my $method="use_${alg}_hash"; |
285
|
|
|
|
|
|
|
|
286
|
27
|
100
|
|
|
|
204
|
if($cert->can($method)) { |
287
|
26
|
|
|
|
|
142
|
$cert->$method(); |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=item * tune_cert_cbs=>{ ...} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
A collection of callbacks to tune a certificate object for signing |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=cut |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
has tune_cert_cbs=>( |
298
|
|
|
|
|
|
|
isa=>HashRef, |
299
|
|
|
|
|
|
|
is=>'ro', |
300
|
|
|
|
|
|
|
default=>sub { |
301
|
|
|
|
|
|
|
return {%TUNE_CERT} |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
); |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=item * mutate_cbs=>{....} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
Transform and Canonization callbacks. The default callbacks are defined in %XML::Sig::OO::MUTATE. |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
Callbacks are usied in the following context |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
$cb->($self,$xpath_element); |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=cut |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
sub _build_canon_coderef { |
316
|
6
|
|
|
6
|
|
15
|
my ($method,$comment)=@_; |
317
|
|
|
|
|
|
|
return sub { |
318
|
57
|
|
|
57
|
|
200
|
my ($self,$x,$node,$nth,$ec14n_inclusive_prefixes)=@_; |
319
|
|
|
|
|
|
|
|
320
|
57
|
100
|
|
|
|
208
|
if ($method eq "toStringEC14N") |
321
|
|
|
|
|
|
|
{ |
322
|
32
|
|
|
|
|
208
|
return $node->$method($comment, undef, $ec14n_inclusive_prefixes); |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
else |
325
|
|
|
|
|
|
|
{ |
326
|
25
|
|
|
|
|
161
|
return $node->$method($comment); |
327
|
|
|
|
|
|
|
} |
328
|
6
|
|
|
|
|
50
|
}; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub _envelope_transform { |
332
|
28
|
|
|
28
|
|
90
|
my ($self,$x,$node,$nth)=@_; |
333
|
|
|
|
|
|
|
|
334
|
28
|
|
|
|
|
132
|
my $xpath=$self->context($self->xpath_Signature,$nth); |
335
|
28
|
|
|
|
|
115
|
my ($target)=$x->findnodes($xpath,$node); |
336
|
28
|
100
|
|
|
|
1678
|
$node->removeChild($target) if defined($target); |
337
|
28
|
|
|
|
|
260
|
return $node->toString; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
our %MUTATE=( |
341
|
|
|
|
|
|
|
'http://www.w3.org/2000/09/xmldsig#enveloped-signature'=>\&_envelope_transform, |
342
|
|
|
|
|
|
|
'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' => _build_canon_coderef('toStringC14N',0), |
343
|
|
|
|
|
|
|
'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments' => _build_canon_coderef('toStringC14N',1), |
344
|
|
|
|
|
|
|
'http://www.w3.org/2006/12/xml-c14n11' => _build_canon_coderef('toStringC14N_v1_1',0), |
345
|
|
|
|
|
|
|
'http://www.w3.org/2006/12/xml-c14n11#WithComments' => _build_canon_coderef('toStringC14N_v1_1',1), |
346
|
|
|
|
|
|
|
'http://www.w3.org/2001/10/xml-exc-c14n#' => _build_canon_coderef('toStringEC14N',0), |
347
|
|
|
|
|
|
|
'http://www.w3.org/2001/10/xml-exc-c14n#WithComments' => _build_canon_coderef('toStringEC14N',1), |
348
|
|
|
|
|
|
|
); |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
has mutate_cbs=>( |
351
|
|
|
|
|
|
|
isa=>HashRef, |
352
|
|
|
|
|
|
|
is=>'ro', |
353
|
|
|
|
|
|
|
default=>sub { return {%MUTATE} }, |
354
|
|
|
|
|
|
|
); |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=back |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=head2 Xpaths |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
The xpaths in this package are not hard coded, each xpath can be defined as an argument to the constructor. Since xml can contain multiple elements with signatures or multiple id elements to sign, most xpaths are prefixed with the $nth signature |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
Some cases the xpaths are used in the following context: |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
(/xpath)[$nth] |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
In special cases like finding a list of transforms or which key, signature, or digest: |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
(//ds::Signature)[$nth]/xpath |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=over 4 |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=item * xpath_SignatureValue=>//ds:SignatureValue |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
Xpath used to find the signature value. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=cut |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
has xpath_SignatureValue=>( |
379
|
|
|
|
|
|
|
isa=>Str, |
380
|
|
|
|
|
|
|
is=>'ro', |
381
|
|
|
|
|
|
|
default=>'//ds:SignatureValue', |
382
|
|
|
|
|
|
|
); |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=item * xpath_SignatureMethod=>'//ds:SignatureMethod/@Algorithm' |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
Xpath used to find the signature method algorithm. |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=cut |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
has xpath_SignatureMethod=>( |
391
|
|
|
|
|
|
|
isa=>Str, |
392
|
|
|
|
|
|
|
is=>'ro', |
393
|
|
|
|
|
|
|
default=>'//ds:SignatureMethod/@Algorithm', |
394
|
|
|
|
|
|
|
); |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=item * xpath_CanonicalizationMethod=>'//ds:CanonicalizationMethod/@Algorithm' |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
Xpath used to find the list of canonicalization method(s). |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=cut |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
has xpath_CanonicalizationMethod=>( |
403
|
|
|
|
|
|
|
is=>Str, |
404
|
|
|
|
|
|
|
is=>'ro', |
405
|
|
|
|
|
|
|
default=>'//ds:CanonicalizationMethod/@Algorithm', |
406
|
|
|
|
|
|
|
); |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=item * xpath_SignedInfo=>'//ds:SignedInfo' |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
Xpath used to find the singed info. |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=cut |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
has xpath_SignedInfo=>( |
415
|
|
|
|
|
|
|
is=>'ro', |
416
|
|
|
|
|
|
|
isa=>Str, |
417
|
|
|
|
|
|
|
default=>'//ds:SignedInfo', |
418
|
|
|
|
|
|
|
); |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=item * xpath_Signature=>'//ds:Signature' |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
Xpath used to fetch the signature value |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=cut |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
has xpath_Signature=>( |
427
|
|
|
|
|
|
|
is=>'ro', |
428
|
|
|
|
|
|
|
isa=>Str, |
429
|
|
|
|
|
|
|
default=>'//ds:Signature' |
430
|
|
|
|
|
|
|
); |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=item * xpath_Transforms=>//ds:Transforms |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
Xpath Transform path |
435
|
|
|
|
|
|
|
=cut |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
has xpath_Transforms=>( |
438
|
|
|
|
|
|
|
isa=>Str, |
439
|
|
|
|
|
|
|
is=>'ro', |
440
|
|
|
|
|
|
|
default=>'//ds:Transforms', |
441
|
|
|
|
|
|
|
); |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=item * xpath_Transform=>'/ds:Transform' |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
Xpath used to find the transform |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=cut |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
has xpath_Transform=>( |
450
|
|
|
|
|
|
|
isa=>Str, |
451
|
|
|
|
|
|
|
is=>'ro', |
452
|
|
|
|
|
|
|
default=>'/ds:Transform' |
453
|
|
|
|
|
|
|
); |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=item * xpath_TransformInclusiveNamespacesPrefixList=>'ec:InclusiveNamespaces/@PrefixList' |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
Xpath used to find the transform Algorithm |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=cut |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
has xpath_TransformInclusiveNamespacesPrefixList=>( |
462
|
|
|
|
|
|
|
isa=>Str, |
463
|
|
|
|
|
|
|
is=>'ro', |
464
|
|
|
|
|
|
|
default=>'ec:InclusiveNamespaces/@PrefixList' |
465
|
|
|
|
|
|
|
); |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=item * xpath_TransformAlgorithm=>'@Algorithm' |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Xpath used to find the transform Algorithm |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=cut |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
has xpath_TransformAlgorithm=>( |
474
|
|
|
|
|
|
|
isa=>Str, |
475
|
|
|
|
|
|
|
is=>'ro', |
476
|
|
|
|
|
|
|
default=>'@Algorithm' |
477
|
|
|
|
|
|
|
); |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=item * xpath_DigestValue=>'//ds:DigestValue' |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Xpath used to fetch the digest value |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=cut |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
has xpath_DigestValue=>( |
486
|
|
|
|
|
|
|
is=>'ro', |
487
|
|
|
|
|
|
|
isa=>Str, |
488
|
|
|
|
|
|
|
default=>'//ds:DigestValue', |
489
|
|
|
|
|
|
|
); |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=item * xpath_DigestMethod=>'//ds:DigestMethod/@Algorithm' |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
Xpath used to find the digest method. |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=cut |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
has xpath_DigestMethod=>( |
498
|
|
|
|
|
|
|
is=>'ro', |
499
|
|
|
|
|
|
|
isa=>Str, |
500
|
|
|
|
|
|
|
default=>'//ds:DigestMethod/@Algorithm', |
501
|
|
|
|
|
|
|
); |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=item * xpath_DigestId=>'//ds:Reference/@URI' |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
Xpath used to find the id of the node that should contain our digest. |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=cut |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
has xpath_DigestId=>( |
510
|
|
|
|
|
|
|
is=>'ro', |
511
|
|
|
|
|
|
|
isa=>Str, |
512
|
|
|
|
|
|
|
default=>'//ds:Reference/@URI', |
513
|
|
|
|
|
|
|
); |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=item * digest_id_convert_cb=>sub { my ($self,$xpath_object,$id)=@_;$id =~ s/^#//;return "//*[\@ID='$id']" } |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Code ref that converts the xpath_DigestId into the xpath lookup ised to find the digest node |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=cut |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
has digest_id_convert_cb=>( |
522
|
|
|
|
|
|
|
isa=>CodeRef, |
523
|
|
|
|
|
|
|
default=>sub { \&_default_digest_id_conversion }, |
524
|
|
|
|
|
|
|
is=>'ro', |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
); |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
sub _default_digest_id_conversion { |
529
|
23
|
|
|
23
|
|
72
|
my ($self,$xpath_object,$id)=@_; |
530
|
23
|
|
|
|
|
183
|
$id=~ s/^#//s; |
531
|
23
|
|
|
|
|
110
|
return "//*[\@ID='$id']"; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=item * xpath_ToSign=>'//[@ID]' |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
Xpath used to find what nodes to sign. |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=cut |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
has xpath_ToSign=>( |
541
|
|
|
|
|
|
|
isa=>Str, |
542
|
|
|
|
|
|
|
is=>'ro', |
543
|
|
|
|
|
|
|
default=>'//*[@ID]', |
544
|
|
|
|
|
|
|
); |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=item * xpath_IdValue=>'//@ID' |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Xpath used to find the value of the current id. |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=cut |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
has xpath_IdValue=>( |
553
|
|
|
|
|
|
|
isa=>Str, |
554
|
|
|
|
|
|
|
is=>'ro', |
555
|
|
|
|
|
|
|
default=>'//@ID', |
556
|
|
|
|
|
|
|
); |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
=item * xpath_Root=>'/' |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
Root of the document expath |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=cut |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
has xpath_Root=>( |
565
|
|
|
|
|
|
|
isa=>Str, |
566
|
|
|
|
|
|
|
is=>'ro', |
567
|
|
|
|
|
|
|
default=>'/', |
568
|
|
|
|
|
|
|
); |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=back |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
=head3 XPaths related to certs |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
This section documents all xpaths/options related to certs. |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=cut |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=over 4 |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=item * xpath_x509Data=>'/ds:KeyInfo/ds:X509Data/ds:X509Certificate' |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
Xpath used to find the x509 cert value. In reality the nth signature will be prepended to this xpath. |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
Actual xpath used: |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
(//ds:Signature)[$nth]/ds:KeyInfo/ds:X509Data/ds:X509Certificate |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=cut |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
has xpath_x509Data=>( |
591
|
|
|
|
|
|
|
is=>'ro', |
592
|
|
|
|
|
|
|
isa=>Str, |
593
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:X509Data/ds:X509Certificate', |
594
|
|
|
|
|
|
|
); |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=item * xpath_RSAKeyValue=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue' |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
Xpath used to find the RSA value tree. |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=cut |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
has xpath_RSAKeyValue=>( |
603
|
|
|
|
|
|
|
is=>'ro', |
604
|
|
|
|
|
|
|
isa=>Str, |
605
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue', |
606
|
|
|
|
|
|
|
); |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=item * xpath_RSA_Modulus=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue/ds:Modulus' |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
Xpath used to find the RSA Modulus. |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=cut |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
has xpath_RSA_Modulus=>( |
615
|
|
|
|
|
|
|
is=>'ro', |
616
|
|
|
|
|
|
|
is=>'rw', |
617
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue/ds:Modulus', |
618
|
|
|
|
|
|
|
); |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=item * xpath_RSA_Exponent=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue/ds:Exponent' |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
Xpath used to find the RSA Exponent. |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=cut |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
has xpath_RSA_Exponent=>( |
627
|
|
|
|
|
|
|
is=>'ro', |
628
|
|
|
|
|
|
|
is=>'rw', |
629
|
|
|
|
|
|
|
isa=>Str, |
630
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:KeyValue/ds:RSAKeyValue/ds:Exponent', |
631
|
|
|
|
|
|
|
); |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
=item * xpath_DSAKeyValue=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue' |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
Xpath used for DSA key tree discovery. |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=cut |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
has xpath_DSAKeyValue=>( |
640
|
|
|
|
|
|
|
is=>'ro', |
641
|
|
|
|
|
|
|
isa=>Str, |
642
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue', |
643
|
|
|
|
|
|
|
); |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=item * xpath_DSA_P=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:P' |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
Xpath used to find DSA_P. |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=cut |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
has xpath_DSA_P=>( |
652
|
|
|
|
|
|
|
is=>'ro', |
653
|
|
|
|
|
|
|
isa=>Str, |
654
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:P', |
655
|
|
|
|
|
|
|
); |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=item * xpath_DSA_Q=>'' |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Xpath used to find DSA_Q. |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=cut |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
has xpath_DSA_Q=>( |
664
|
|
|
|
|
|
|
is=>'ro', |
665
|
|
|
|
|
|
|
isa=>Str, |
666
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:Q', |
667
|
|
|
|
|
|
|
); |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=item * xpath_DSA_G=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:G' |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
Xpath used to find DSA_G. |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=cut |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
has xpath_DSA_G=>( |
676
|
|
|
|
|
|
|
is=>'ro', |
677
|
|
|
|
|
|
|
isa=>Str, |
678
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:G', |
679
|
|
|
|
|
|
|
); |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=item * xpath_DSA_Y=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:Y' |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
Xpath used to find DSA_Y |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=cut |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
has xpath_DSA_Y=>( |
688
|
|
|
|
|
|
|
is=>'ro', |
689
|
|
|
|
|
|
|
isa=>Str, |
690
|
|
|
|
|
|
|
default=>'/ds:KeyInfo/ds:KeyValue/ds:DSAKeyValue/ds:Y', |
691
|
|
|
|
|
|
|
); |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
=back |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=head3 OO Signing Options |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
The following Signature options can be passed to the constructor object. |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
=over 4 |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
=item * key_file=>'path/to/my.key' |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
Key file only used when signing. |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=cut |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
has key_file=>( |
708
|
|
|
|
|
|
|
isa=>Str, |
709
|
|
|
|
|
|
|
required=>0, |
710
|
|
|
|
|
|
|
is=>'ro', |
711
|
|
|
|
|
|
|
); |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
=item * envelope_method=>"http://www.w3.org/2000/09/xmldsig#enveloped-signature" |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
Sets the envelope method; This value most likely is the only valid value. |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=cut |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
has envelope_method=>( |
720
|
|
|
|
|
|
|
isa=>Str, |
721
|
|
|
|
|
|
|
is=>'ro', |
722
|
|
|
|
|
|
|
default=>"http://www.w3.org/2000/09/xmldsig#enveloped-signature", |
723
|
|
|
|
|
|
|
); |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
#=item * canon_method=>'http://www.w3.org/2001/10/xml-exc-c14n#' |
726
|
|
|
|
|
|
|
=item * canon_method=>'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments' |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Sets the canonization method used when signing the code |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=cut |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
has canon_method=>( |
733
|
|
|
|
|
|
|
isa=>Str, |
734
|
|
|
|
|
|
|
#default=>"http://www.w3.org/2001/10/xml-exc-c14n#", |
735
|
|
|
|
|
|
|
default=>"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", |
736
|
|
|
|
|
|
|
is=>'ro', |
737
|
|
|
|
|
|
|
); |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
=item * tag_namespace=>'ds' |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
Default namespace of the tags being created. This must be defined in $self->namespaces. |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
=cut |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
has tag_namespace=>( |
746
|
|
|
|
|
|
|
isa=>Str, |
747
|
|
|
|
|
|
|
default=>'ds', |
748
|
|
|
|
|
|
|
is=>'ro', |
749
|
|
|
|
|
|
|
); |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
=item * sign_cert=>$cert_object |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
Optional: The Certificate object used to sign xml. If this option is set it is recomended that you set the "key_type" option as well. |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
=cut |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
has sign_cert=>( |
758
|
|
|
|
|
|
|
isa=>Object, |
759
|
|
|
|
|
|
|
is=>'rw', |
760
|
|
|
|
|
|
|
required=>0, |
761
|
|
|
|
|
|
|
lazy=>1, |
762
|
|
|
|
|
|
|
); |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=item * cert_file=>'/path/to/cert.pem' |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
The path that contains the cert file used for signing. |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
=cut |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
has cert_file=>( |
771
|
|
|
|
|
|
|
isa=>sub { |
772
|
|
|
|
|
|
|
my ($file)=@_; |
773
|
|
|
|
|
|
|
croak "$file must be defined" unless defined($file); |
774
|
|
|
|
|
|
|
croak "$file must be readable" unless -r $file; |
775
|
|
|
|
|
|
|
}, |
776
|
|
|
|
|
|
|
is=>'rw', |
777
|
|
|
|
|
|
|
required=>0, |
778
|
|
|
|
|
|
|
lazy=>1, |
779
|
|
|
|
|
|
|
); |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=item * cert_string=>undef |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
This optional argument lets you define the x509 pem text that will be used to generate the x509 portion of the xml. |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
=cut |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
has cert_string=>( |
788
|
|
|
|
|
|
|
is=>'rw', |
789
|
|
|
|
|
|
|
required=>0, |
790
|
|
|
|
|
|
|
lazy=>1, |
791
|
|
|
|
|
|
|
); |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=back |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=cut |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
sub BUILD { |
798
|
25
|
|
|
25
|
0
|
20928
|
my ($self)=@_; |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
# sanity check dsa signature method |
801
|
25
|
50
|
33
|
|
|
497
|
croak 'dsa key types only work with signature_method: http://www.w3.org/2000/09/xmldsig#dsa-sha1' |
802
|
|
|
|
|
|
|
if $self->key_type eq 'dsa' && $self->signature_method ne 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'; |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
|
805
|
25
|
50
|
|
|
|
446
|
croak "namespaces does not contain: ".$self->tag_namespace unless exists $self->namespaces->{$self->tag_namespace}; |
806
|
25
|
50
|
|
|
|
159
|
croak $self->signature_method." is an unsupported signature method" unless exists $self->tune_cert_cbs->{$self->signature_method}; |
807
|
25
|
100
|
66
|
|
|
510
|
if(defined($self->key_file) && !defined($self->sign_cert)) { |
808
|
5
|
|
|
|
|
84
|
my $result=$self->load_cert_from_file($self->key_file); |
809
|
5
|
50
|
|
|
|
1268
|
croak $result unless $result; |
810
|
5
|
|
|
|
|
293
|
my ($key_type,$cert)=@{$result->get_data}{qw(type cert)}; |
|
5
|
|
|
|
|
21
|
|
811
|
5
|
|
|
|
|
121
|
$self->sign_cert($cert); |
812
|
5
|
|
|
|
|
331
|
$self->key_type($key_type); |
813
|
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
=head1 OO Methods |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
=head2 my $xpath=$self->build_xpath(undef|$xml,{ns=>'url'}|undef); |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
Creates a new xpath object based on our current object state. |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
=cut |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
sub build_xpath { |
825
|
69
|
|
|
69
|
1
|
2806
|
my ($self,$xml,$ns)=@_; |
826
|
69
|
50
|
|
|
|
355
|
$xml=$self->xml unless defined($xml); |
827
|
69
|
50
|
|
|
|
335
|
$ns=$self->namespaces unless defined($ns); |
828
|
69
|
|
|
|
|
493
|
my $p=XML::LibXML->new(clean_namespaces=>1); |
829
|
69
|
|
|
|
|
6194
|
my $dom = $p->parse_string( $xml); |
830
|
69
|
|
|
|
|
22054
|
my $x=XML::LibXML::XPathContext->new($dom); |
831
|
69
|
|
|
|
|
192
|
while(my ($key,$value)=each %{$ns}) { |
|
276
|
|
|
|
|
923
|
|
832
|
207
|
|
|
|
|
1021
|
$x->registerNs($key,$value); |
833
|
|
|
|
|
|
|
} |
834
|
69
|
|
|
|
|
457
|
return $x; |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
=head2 my $result=$self->validate; |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
Returns a Data::Result Object. When true validation passed, when false it contains why validation failed. |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
A better use case would be this: |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
my $result=$self->validate; |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
if($result) { |
846
|
|
|
|
|
|
|
print "everything checks out\n"; |
847
|
|
|
|
|
|
|
} else { |
848
|
|
|
|
|
|
|
foreach my $chunk (@{$result->get_data}) { |
849
|
|
|
|
|
|
|
my ($nth,$signature,$digest)=@{$chunk}{qw(nth signature digest)}; |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
print "Results for processing chunk $nth\n"; |
852
|
|
|
|
|
|
|
print "Signature State: ".($signature ? "OK\n" : "Failed, error was $signature\n"; |
853
|
|
|
|
|
|
|
print "Digest State: ".($digest ? "OK\n" : "Failed, error was $digest\n"; |
854
|
|
|
|
|
|
|
} |
855
|
|
|
|
|
|
|
} |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
=cut |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
sub validate { |
860
|
18
|
|
|
18
|
1
|
131
|
my ($self)=@_; |
861
|
|
|
|
|
|
|
|
862
|
18
|
|
|
|
|
70
|
my $total=$self->build_xpath->findnodes($self->xpath_Signature)->size; |
863
|
|
|
|
|
|
|
|
864
|
18
|
|
|
|
|
1933
|
my $list=[]; |
865
|
18
|
|
|
|
|
1285
|
my $result=Data::Result->new(data=>$list,is_true=>1); |
866
|
18
|
|
|
|
|
3682
|
for(my $nth=1;$nth <= $total;++$nth) { |
867
|
21
|
|
|
|
|
120
|
my $sig=$self->verify_signature(undef,$nth); |
868
|
21
|
|
|
|
|
7141
|
my $digest=$self->verify_digest(undef,$nth); |
869
|
21
|
50
|
33
|
|
|
6249
|
$result->is_true(0) unless $sig && $digest; |
870
|
21
|
|
|
|
|
2650
|
my $ref={ |
871
|
|
|
|
|
|
|
nth=>$nth, |
872
|
|
|
|
|
|
|
signature=>$sig, |
873
|
|
|
|
|
|
|
digest=>$digest, |
874
|
|
|
|
|
|
|
}; |
875
|
21
|
|
|
|
|
155
|
push @$list,$ref; |
876
|
|
|
|
|
|
|
} |
877
|
18
|
100
|
|
|
|
40
|
$result->is_true(0) if $#{$list}==-1; |
|
18
|
|
|
|
|
92
|
|
878
|
18
|
|
|
|
|
250
|
return $result; |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
} |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
=head2 my $result=$self->verify_digest($nth) |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
Returns a Data::Result object: when true, the signature was verified, when false it contains why it failed. |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=cut |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
sub verify_digest { |
889
|
22
|
|
|
22
|
1
|
145
|
my ($self,$x,$nth)=@_; |
890
|
|
|
|
|
|
|
|
891
|
22
|
100
|
|
|
|
149
|
$x=$self->build_xpath unless defined($x); |
892
|
|
|
|
|
|
|
|
893
|
22
|
|
|
|
|
121
|
my $result=$self->get_digest_value($x,$nth); |
894
|
22
|
50
|
|
|
|
4178
|
return $result unless $result; |
895
|
22
|
|
|
|
|
1175
|
my $value=$result->get_data; |
896
|
|
|
|
|
|
|
|
897
|
22
|
|
|
|
|
193
|
$result=$self->get_digest_method($x,$nth); |
898
|
22
|
50
|
|
|
|
5944
|
return $result unless $result; |
899
|
22
|
|
|
|
|
1154
|
my $method=$result->get_data; |
900
|
|
|
|
|
|
|
|
901
|
22
|
|
|
|
|
171
|
$result=$self->get_digest_node($x,$nth); |
902
|
22
|
50
|
|
|
|
5814
|
return $result unless $result; |
903
|
22
|
|
|
|
|
1100
|
my $node=$result->get_data; |
904
|
|
|
|
|
|
|
|
905
|
22
|
|
|
|
|
229
|
$result=$self->do_transforms($x,$node,$nth); |
906
|
22
|
50
|
|
|
|
7013
|
return $result unless $result; |
907
|
22
|
|
|
|
|
1080
|
my $xml=$result->get_data; |
908
|
|
|
|
|
|
|
|
909
|
22
|
|
|
|
|
234
|
my $cmp=$self->digest_cbs->{$method}->($self,$xml); |
910
|
22
|
|
|
|
|
103
|
$cmp=~ s/\s+//sg; |
911
|
22
|
50
|
|
|
|
107
|
return new_false Data::Result("orginal digest: $value ne $cmp") unless $value eq $cmp; |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
# if we get here our digest checks out |
914
|
22
|
|
|
|
|
87
|
return new_true Data::Result("Ok"); |
915
|
|
|
|
|
|
|
} |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=head2 my $result=$self->get_transforms($xpath_object,$nth) |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
Returns a Data::Reslt object, when true it contains an array ref that contains each digest transform, when false it contains why it failed. |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
Please note, the xpath generate is a concatination of $self->context($self->xpath_Transforms,$nth).$self->xpath_Transform, so keep that in mind when trying to change how transforms are looked up. |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
=cut |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
sub get_transforms { |
926
|
23
|
|
|
23
|
1
|
721
|
my ($self,$x,$nth)=@_; |
927
|
|
|
|
|
|
|
|
928
|
23
|
|
|
|
|
177
|
my $xpath=$self->context($self->xpath_Transforms,$nth).$self->xpath_Transform; |
929
|
|
|
|
|
|
|
|
930
|
23
|
|
|
|
|
86
|
my $transforms=$x->findnodes($xpath); |
931
|
23
|
|
|
|
|
1248
|
my $data=[]; |
932
|
|
|
|
|
|
|
|
933
|
23
|
|
|
|
|
97
|
foreach my $transform ($transforms->get_nodelist) { |
934
|
46
|
|
|
|
|
300
|
my $algo = $x->findvalue($self->xpath_TransformAlgorithm, $transform); |
935
|
|
|
|
|
|
|
|
936
|
46
|
|
|
|
|
3245
|
my $prefixes = []; |
937
|
46
|
|
|
|
|
81
|
my $pfx=[]; |
938
|
46
|
100
|
66
|
|
|
291
|
if ($algo eq TRANSFORM_EXC_C14N or $algo eq TRANSFORM_EXC_C14N_COMMENTS) { |
939
|
17
|
|
|
|
|
81
|
my $rawprefixes = $x->findvalue($self->xpath_TransformInclusiveNamespacesPrefixList, $transform); |
940
|
|
|
|
|
|
|
|
941
|
17
|
100
|
|
|
|
950
|
if ($rawprefixes ne "") { |
942
|
1
|
|
|
|
|
5
|
@$prefixes = split(' ', $rawprefixes); |
943
|
|
|
|
|
|
|
} |
944
|
17
|
100
|
|
|
|
56
|
$pfx = $rawprefixes ? [prefixes => $prefixes] : [ ] ; |
945
|
|
|
|
|
|
|
} |
946
|
|
|
|
|
|
|
|
947
|
46
|
|
|
|
|
223
|
push @$data, { algorithm => $algo, @$pfx }; |
948
|
|
|
|
|
|
|
} |
949
|
|
|
|
|
|
|
|
950
|
23
|
50
|
|
|
|
50
|
return new_false Data::Result("Failed to find transforms in xpath: $xpath") unless $#{$data}>-1; |
|
23
|
|
|
|
|
99
|
|
951
|
23
|
|
|
|
|
84
|
return new_true Data::Result($data); |
952
|
|
|
|
|
|
|
} |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
=head2 my $result=$self->get_digest_node($xpath_object) |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
Returns a Data::Result Object, when true it contains the Digest Node, when false it contains why it failed. |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
=cut |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
sub get_digest_node { |
961
|
23
|
|
|
23
|
1
|
700
|
my ($self,$x,$nth)=@_; |
962
|
23
|
|
|
|
|
121
|
my ($id)=$x->findvalue($self->context($self->xpath_DigestId,$nth)); |
963
|
23
|
50
|
|
|
|
1738
|
return new_false Data::Result("Could not find our digest node id in xpath: ".$self->xpath_DigestId) unless defined($id); |
964
|
23
|
|
|
|
|
140
|
my $next_xpath=$self->digest_id_convert_cb->($self,$x,$id); |
965
|
|
|
|
|
|
|
|
966
|
23
|
|
|
|
|
108
|
my ($node)=$x->findnodes($next_xpath); |
967
|
23
|
50
|
|
|
|
1763
|
return new_false Data::Result("Could not find our digest node in xpath: $next_xpath") unless defined($node); |
968
|
|
|
|
|
|
|
|
969
|
23
|
|
|
|
|
104
|
return new_true Data::Result($node); |
970
|
|
|
|
|
|
|
} |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
=head2 my $result=$self->get_digest_method($xpath_object,$nth) |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
Returns a Data::Result Object, when true it contains the Digest Method |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
=cut |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
sub get_digest_method { |
979
|
23
|
|
|
23
|
1
|
2068
|
my ($self,$x,$nth)=@_; |
980
|
23
|
|
|
|
|
110
|
my $xpath=$self->context($self->xpath_DigestMethod,$nth); |
981
|
23
|
|
|
|
|
84
|
my ($digest_value)=$x->findvalue($xpath); |
982
|
23
|
50
|
|
|
|
1923
|
return new_false Data::Result("Failed to find Digest Method in xpath: $xpath") unless defined($digest_value); |
983
|
23
|
50
|
|
|
|
177
|
return new_false Data::Result("Unsupported Digest Method: $digest_value") unless exists $self->digest_cbs->{$digest_value}; |
984
|
23
|
|
|
|
|
95
|
return new_true Data::Result($digest_value); |
985
|
|
|
|
|
|
|
} |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
=head2 my $result=$self->get_digest_value($xpath_object,$nth) |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
Returns a Data::Result Object, when true it contains the Digest Value. |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
=cut |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
sub get_digest_value { |
994
|
23
|
|
|
23
|
1
|
881
|
my ($self,$x,$nth)=@_; |
995
|
23
|
|
|
|
|
134
|
my ($digest_value)=$x->findvalue($self->context($self->xpath_DigestValue,$nth)); |
996
|
23
|
50
|
|
|
|
2010
|
return new_false Data::Result("Failed to find Digest Value in xpath: ".$self->xpath_DigestValue) unless defined($digest_value); |
997
|
23
|
|
|
|
|
450
|
$digest_value=~ s/\s+//sg; |
998
|
23
|
|
|
|
|
101
|
return new_true Data::Result($digest_value); |
999
|
|
|
|
|
|
|
} |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
=head2 my $result=$self->verify_signature($nth); |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
Returns a Data::Result Object, when true the signature was validated, when fails it contains why it failed. |
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
=cut |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
sub verify_signature { |
1008
|
22
|
|
|
22
|
1
|
139
|
my ($self,$x,$nth)=@_; |
1009
|
22
|
100
|
|
|
|
101
|
$x=$self->build_xpath unless defined($x); |
1010
|
|
|
|
|
|
|
|
1011
|
22
|
|
|
|
|
128
|
my $pos=$self->context($self->xpath_Signature,$nth); |
1012
|
22
|
|
|
|
|
94
|
my $x509_path=$pos.$self->xpath_x509Data; |
1013
|
22
|
|
|
|
|
93
|
my $rsa_path=$pos.$self->xpath_RSAKeyValue; |
1014
|
22
|
|
|
|
|
87
|
my $dsa_path=$pos.$self->xpath_DSAKeyValue; |
1015
|
22
|
100
|
|
|
|
121
|
if(my $string=$x->findvalue($x509_path)) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1016
|
17
|
50
|
|
|
|
1874
|
return new_false Data::Result("Found more than one x509 node in xpath: ".$self->xpath_x509Data) unless defined($string); |
1017
|
17
|
|
|
|
|
89
|
return $self->verify_x509_sig($x,$string,$nth); |
1018
|
|
|
|
|
|
|
} elsif($x->findvalue($rsa_path)) { |
1019
|
4
|
|
|
|
|
648
|
return $self->verify_rsa($x,$string,$nth); |
1020
|
|
|
|
|
|
|
} elsif($x->findvalue($dsa_path)) { |
1021
|
1
|
|
|
|
|
218
|
return $self->verify_dsa($x,$string,$nth); |
1022
|
|
|
|
|
|
|
} else { |
1023
|
0
|
|
|
|
|
0
|
return new_false Data::Result("Currently Unsupported certificate method"); |
1024
|
|
|
|
|
|
|
} |
1025
|
|
|
|
|
|
|
} |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
=head2 my $result=$self->verify_dsa($x,$string,$nth) |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
Returns a Data::Result object, when true it validated the DSA signature. |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
=cut |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
sub verify_dsa { |
1034
|
1
|
|
|
1
|
1
|
4
|
my ($self,$x,$string,$nth)=@_; |
1035
|
|
|
|
|
|
|
|
1036
|
1
|
|
|
|
|
6
|
my $pos=$self->context($self->xpath_Signature,$nth); |
1037
|
1
|
|
|
|
|
9
|
my $dsa_pub = Crypt::OpenSSL::DSA->new(); |
1038
|
|
|
|
|
|
|
|
1039
|
1
|
|
|
|
|
4
|
foreach my $key (qw(p q g y)) { |
1040
|
4
|
|
|
|
|
13
|
my $method="xpath_DSA_".uc($key); |
1041
|
4
|
|
|
|
|
18
|
my $xpath=$pos.$self->$method(); |
1042
|
4
|
|
|
|
|
12
|
my $value=$x->findvalue($xpath); |
1043
|
|
|
|
|
|
|
|
1044
|
4
|
50
|
|
|
|
274
|
return new_false Data::Result("Did not find DSA $key in xpath: $xpath") unless defined($value); |
1045
|
4
|
|
|
|
|
58
|
my $opt="set_$key"; |
1046
|
4
|
|
|
|
|
11
|
my $set=decode_base64(_trim($value)); |
1047
|
4
|
100
|
|
|
|
47
|
$dsa_pub->can($opt) ? $dsa_pub->$opt($set) : $dsa_pub->set_pub_key($set); |
1048
|
|
|
|
|
|
|
} |
1049
|
|
|
|
|
|
|
|
1050
|
1
|
|
|
|
|
7
|
my $result=$self->tune_cert_and_get_sig($x,$nth,$dsa_pub); |
1051
|
1
|
|
|
|
|
270
|
my $ref=$result->get_data; |
1052
|
|
|
|
|
|
|
# DSA signatures are limited to a message body of 20 characters, so a sha1 digest is taken |
1053
|
1
|
50
|
|
|
|
294
|
return new_true Data::Result("OK") if $dsa_pub->verify(sha1($ref->{xml}),$ref->{sig}); |
1054
|
|
|
|
|
|
|
|
1055
|
0
|
|
|
|
|
0
|
return new_false Data::Result("Failed to validate DSA Signature"); |
1056
|
|
|
|
|
|
|
} |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
=head2 my $xpath_string=$self->context($xpath,$nth) |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
Returns an xpath wrapped in the nth instance syntax. |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
Example |
1063
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
my $xpath="//something" |
1065
|
|
|
|
|
|
|
my $nth=2; |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
my $xpath_string=$self->context($xpath,$nth); |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
$xpath_string eq '(//something)[2]'; |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
Note: if nth is not set it defaults to 1 |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
=cut |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
sub context { |
1077
|
255
|
|
|
255
|
1
|
660
|
my ($self,$xpath,$nth)=@_; |
1078
|
255
|
100
|
|
|
|
955
|
$nth=1 unless looks_like_number($nth); |
1079
|
255
|
|
|
|
|
1256
|
return "($xpath)[$nth]"; |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
=head2 my $result=$self->get_sig_canon($x,$nth) |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the canon xml of the $nth signature node. |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
=cut |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
sub get_sig_canon { |
1089
|
22
|
|
|
22
|
1
|
61
|
my ($self,$x,$nth)=@_; |
1090
|
22
|
|
|
|
|
88
|
my $result=$self->get_signed_info_node($x,$nth); |
1091
|
22
|
|
|
|
|
3957
|
my $signed_info_node=$result->get_data; |
1092
|
22
|
50
|
|
|
|
130
|
return $result unless $result; |
1093
|
|
|
|
|
|
|
|
1094
|
22
|
|
|
|
|
1115
|
return $self->do_canon($x,$signed_info_node,$nth); |
1095
|
|
|
|
|
|
|
} |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
=head2 my $result=$self->verify_x509_sig($x,$string,$nth) |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
Returns a Data::Result Object, when true the x509 signature was validated. |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
=cut |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
sub verify_x509_sig { |
1104
|
17
|
|
|
17
|
1
|
68
|
my ($self,$x,$string,$nth)=@_; |
1105
|
|
|
|
|
|
|
|
1106
|
17
|
|
|
|
|
73
|
my $x509=$self->clean_x509($string); |
1107
|
17
|
|
|
|
|
1988
|
my $cert=Crypt::OpenSSL::X509->new_from_string($x509); |
1108
|
|
|
|
|
|
|
|
1109
|
17
|
100
|
|
|
|
119
|
if(defined($self->cacert)) { |
1110
|
1
|
|
|
|
|
408
|
my $ca=Crypt::OpenSSL::VerifyX509->new($self->cacert); |
1111
|
1
|
|
|
|
|
4
|
my $result; |
1112
|
1
|
50
|
|
|
|
3
|
eval {$result=new_false Data::Result("Could not verify the x509 cert against ".$self->cacert) unless $ca->verify($cert)}; |
|
1
|
|
|
|
|
529
|
|
1113
|
1
|
50
|
|
|
|
6
|
if($@) { |
1114
|
0
|
|
|
|
|
0
|
return new_false Data::Result("Error using cert file: ".$self->cacert."error was: $@"); |
1115
|
|
|
|
|
|
|
} |
1116
|
1
|
50
|
|
|
|
45
|
return $result if defined($result); |
1117
|
|
|
|
|
|
|
} |
1118
|
|
|
|
|
|
|
|
1119
|
17
|
|
|
|
|
1284
|
my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($cert->pubkey); |
1120
|
|
|
|
|
|
|
|
1121
|
17
|
|
|
|
|
1411
|
my $result=$self->tune_cert_and_get_sig($x,$nth,$rsa_pub); |
1122
|
17
|
|
|
|
|
4189
|
my $ref=$result->get_data; |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
return Data::Result->new_false("x509 signature check failed, becase our generated signature did not match the one stored in the xml") |
1125
|
17
|
50
|
|
|
|
2168
|
unless $rsa_pub->verify($ref->{xml},$ref->{sig}); |
1126
|
|
|
|
|
|
|
|
1127
|
17
|
|
|
|
|
78
|
return new_true Data::Result("Ok"); |
1128
|
|
|
|
|
|
|
} |
1129
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
=head2 my $result=$self->tune_cert_and_get_sig($x,$nth,$cert) |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the following hashref |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
Structure: |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
cert: the tuned cert |
1137
|
|
|
|
|
|
|
sig: the binary signature to verify |
1138
|
|
|
|
|
|
|
xml: the xml to be verified against the signature |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
=cut |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
sub tune_cert_and_get_sig { |
1143
|
22
|
|
|
22
|
1
|
90
|
my ($self,$x,$nth,$cert)=@_; |
1144
|
|
|
|
|
|
|
|
1145
|
22
|
|
|
|
|
114
|
my $result=$self->get_signature_method($x,$nth,$cert); |
1146
|
22
|
50
|
|
|
|
4353
|
return $result unless $result; |
1147
|
22
|
|
|
|
|
1265
|
my $method=$result->get_data; |
1148
|
|
|
|
|
|
|
|
1149
|
22
|
|
|
|
|
177
|
$result=$self->tune_cert($cert,$method); |
1150
|
22
|
50
|
|
|
|
6100
|
return $result unless $result; |
1151
|
|
|
|
|
|
|
|
1152
|
22
|
|
|
|
|
1103
|
$result=$self->get_sig_canon($x,$nth); |
1153
|
22
|
50
|
|
|
|
9104
|
return $result unless $result; |
1154
|
22
|
|
|
|
|
1079
|
my $xml=$result->get_data; |
1155
|
|
|
|
|
|
|
|
1156
|
22
|
|
|
|
|
159
|
$result=$self->get_signature_value($x,$nth); |
1157
|
22
|
50
|
|
|
|
5867
|
return $result unless $result; |
1158
|
22
|
|
|
|
|
1092
|
my $sig=$result->get_data; |
1159
|
|
|
|
|
|
|
|
1160
|
22
|
|
|
|
|
210
|
return new_true Data::Result({ |
1161
|
|
|
|
|
|
|
sig=>$sig, |
1162
|
|
|
|
|
|
|
xml=>$xml, |
1163
|
|
|
|
|
|
|
cert=>$cert, |
1164
|
|
|
|
|
|
|
}); |
1165
|
|
|
|
|
|
|
} |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
=head2 my $result=$self->verify_rsa($x,$nth) |
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
Returns a Data::Result Object, when true the the rsa key verification passed. |
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
=cut |
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
sub verify_rsa { |
1174
|
4
|
|
|
4
|
1
|
17
|
my ($self,$x,$nth)=@_; |
1175
|
4
|
|
|
|
|
19
|
my $pos=$self->context($self->xpath_Signature,$nth); |
1176
|
4
|
|
|
|
|
22
|
my $xpath=$pos.$self->xpath_RSA_Modulus; |
1177
|
|
|
|
|
|
|
|
1178
|
4
|
|
|
|
|
17
|
my $mod=_trim($x->findvalue($xpath)); |
1179
|
4
|
50
|
|
|
|
19
|
return new_false Data::Result("Failed to find rsa modulus in xpath: $xpath") if $mod=~ m/^\s*$/s; |
1180
|
|
|
|
|
|
|
|
1181
|
4
|
|
|
|
|
184
|
$xpath=$pos.$self->xpath_RSA_Exponent; |
1182
|
4
|
|
|
|
|
43
|
my $exp=_trim($x->findvalue($xpath)); |
1183
|
4
|
50
|
|
|
|
16
|
return new_false Data::Result("Failed to find rsa exponent in xpath: $xpath") if $exp=~ m/^\s*$/s; |
1184
|
|
|
|
|
|
|
|
1185
|
4
|
|
|
|
|
122
|
my $m = Crypt::OpenSSL::Bignum->new_from_bin(decode_base64($mod)); |
1186
|
4
|
|
|
|
|
22
|
my $e = Crypt::OpenSSL::Bignum->new_from_bin(decode_base64($exp)); |
1187
|
|
|
|
|
|
|
|
1188
|
4
|
|
|
|
|
122
|
my $rsa_pub = Crypt::OpenSSL::RSA->new_key_from_parameters( $m, $e ); |
1189
|
|
|
|
|
|
|
|
1190
|
4
|
|
|
|
|
706
|
my $result=$self->tune_cert_and_get_sig($x,$nth,$rsa_pub); |
1191
|
4
|
|
|
|
|
1059
|
my $ref=$result->get_data; |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
return Data::Result->new_false("rsa signature check failed, becase our generated signature did not match the one stored in the xml") |
1194
|
4
|
50
|
|
|
|
1020
|
unless $rsa_pub->verify($ref->{xml},$ref->{sig}); |
1195
|
|
|
|
|
|
|
|
1196
|
4
|
|
|
|
|
21
|
return new_true Data::Result("Ok"); |
1197
|
|
|
|
|
|
|
} |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
=head2 my $result=$self->do_transforms($xpath_object,$node_to_transform,$nth_node); |
1200
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
Retruns a Data::Result Object, when true it contains the xml string of the context node. |
1202
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
=cut |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
sub do_transforms { |
1206
|
22
|
|
|
22
|
1
|
76
|
my ($self,$x,$target,$nth)=@_; |
1207
|
22
|
|
|
|
|
85
|
my $result=$self->get_transforms($x,$nth); |
1208
|
22
|
50
|
|
|
|
4482
|
return $result unless $result; |
1209
|
22
|
|
|
|
|
1096
|
my @todo=@{$result->get_data}; |
|
22
|
|
|
|
|
57
|
|
1210
|
22
|
|
|
|
|
124
|
my $xml; |
1211
|
22
|
|
|
|
|
83
|
foreach my $transform (@todo) { |
1212
|
44
|
|
|
|
|
2140
|
my $algorithm = $transform->{algorithm}; |
1213
|
44
|
|
|
|
|
99
|
my @prefixes = $transform->{prefixes}; |
1214
|
44
|
|
|
|
|
163
|
my $result=$self->transform($x,$target,$algorithm,$nth,@prefixes); |
1215
|
44
|
50
|
|
|
|
17273
|
return $result unless $result; |
1216
|
44
|
|
|
|
|
2186
|
$xml=$result->get_data; |
1217
|
|
|
|
|
|
|
} |
1218
|
22
|
|
|
|
|
2215
|
return new_true Data::Result($xml); |
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
=head2 my $result=$self->do_canon($xpath_object,$node_to_transform,$nth_node); |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
Returns a Data::Result Object, when true it contains the canonized string. |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
=cut |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
sub do_canon { |
1228
|
22
|
|
|
22
|
1
|
88
|
my ($self,$x,$target,$nth)=@_; |
1229
|
22
|
|
|
|
|
110
|
my $result=$self->get_canon($x,$nth); |
1230
|
22
|
50
|
|
|
|
3882
|
return $result unless $result; |
1231
|
22
|
|
|
|
|
1069
|
my $todo=$result->get_data; |
1232
|
22
|
|
|
|
|
100
|
my $xml; |
1233
|
22
|
|
|
|
|
38
|
foreach my $transform (@{$todo}) { |
|
22
|
|
|
|
|
86
|
|
1234
|
22
|
|
|
|
|
91
|
my $result=$self->transform($x,$target,$transform,$nth,undef); |
1235
|
22
|
50
|
|
|
|
8927
|
return $result unless $result; |
1236
|
22
|
|
|
|
|
1070
|
$xml=$result->get_data; |
1237
|
|
|
|
|
|
|
} |
1238
|
22
|
|
|
|
|
2062
|
return new_true Data::Result($xml); |
1239
|
|
|
|
|
|
|
} |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
=head2 my $result=$self->get_canon($xpath_object,$nth) |
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
Returns a Data::Result Object, when true it contains an array ref of the canon methods. |
1244
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
Special note, the xpath is generated as follows |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
my $xpath=$self->context($self->xpath_SignedInfo,$nth).$self->xpath_CanonicalizationMethod; |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
=cut |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
sub get_canon { |
1252
|
22
|
|
|
22
|
1
|
64
|
my ($self,$x,$nth)=@_; |
1253
|
|
|
|
|
|
|
|
1254
|
22
|
|
|
|
|
81
|
my $xpath=$self->context($self->xpath_SignedInfo,$nth).$self->xpath_CanonicalizationMethod; |
1255
|
22
|
|
|
|
|
80
|
my $nodes=$x->find($xpath); |
1256
|
22
|
|
|
|
|
1114
|
my $data=[]; |
1257
|
22
|
|
|
|
|
97
|
foreach my $att ($nodes->get_nodelist) { |
1258
|
22
|
|
|
|
|
286
|
push @$data,$att->value; |
1259
|
|
|
|
|
|
|
} |
1260
|
22
|
50
|
|
|
|
46
|
return new_false Data::Result("No canonization methods found in xpath: $xpath") unless $#{$data} >-1; |
|
22
|
|
|
|
|
96
|
|
1261
|
22
|
|
|
|
|
77
|
return new_true Data::Result($data); |
1262
|
|
|
|
|
|
|
} |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
=head2 my $result=$self->get_signature_value($xpath_object,$nth) |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the base64 decoded signature |
1267
|
|
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
=cut |
1269
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
sub get_signature_value { |
1271
|
22
|
|
|
22
|
1
|
78
|
my ($self,$x,$nth)=@_; |
1272
|
22
|
|
|
|
|
120
|
my ($encoded)=$x->findvalue($self->context($self->xpath_SignatureValue,$nth)); |
1273
|
22
|
50
|
|
|
|
2291
|
return new_false Data::Result("Signature Value was not found in xpath: ".$self->xpath_SignatureValue) unless defined($encoded); |
1274
|
|
|
|
|
|
|
|
1275
|
22
|
|
|
|
|
563
|
$encoded=~ s/\s+//sg; |
1276
|
22
|
|
|
|
|
291
|
return new_true Data::Result(decode_base64($encoded)); |
1277
|
|
|
|
|
|
|
} |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=head2 my $result=$self->get_signed_info_node($xpath_object,$nth); |
1280
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
Given $xpath_object, Returns a Data::Result when true it will contains the signed info node |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
=cut |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
sub get_signed_info_node { |
1286
|
29
|
|
|
29
|
1
|
5960
|
my ($self,$x,$nth)=@_; |
1287
|
|
|
|
|
|
|
|
1288
|
29
|
|
|
|
|
155
|
my ($node)=$x->findnodes($self->context($self->xpath_SignedInfo,$nth)); |
1289
|
29
|
50
|
|
|
|
1420
|
return new_false Data::Result("Signature node(s) not found in xpath: ".$self->xpath_Signature) unless defined($node); |
1290
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
# leave it up to our transform! |
1292
|
29
|
|
|
|
|
113
|
return new_true Data::Result($node); |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
} |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
=head2 my $result=$self->get_signature_method($xpath_object,$nth_node,$cert|undef) |
1297
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the SignatureMethod. If $cert is passed in, it will cert the hashing mode for the cert |
1299
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
=cut |
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
sub get_signature_method { |
1303
|
22
|
|
|
22
|
1
|
89
|
my ($self,$x,$nth,$cert)=@_; |
1304
|
|
|
|
|
|
|
|
1305
|
22
|
|
|
|
|
103
|
my ($method_url)=$x->findvalue($self->context($self->xpath_SignatureMethod,$nth)); |
1306
|
22
|
50
|
|
|
|
1866
|
return new_false Data::Result("SignatureMethod not found in xpath: ".$self->xpath_SignatureMethod) unless defined($method_url); |
1307
|
|
|
|
|
|
|
|
1308
|
22
|
|
|
|
|
126
|
return new_true Data::Result($method_url); |
1309
|
|
|
|
|
|
|
} |
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
=head2 my $result=$self->tune_cert($cert,$method) |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
Returns a Data::Result Object, when true Sets the hashing method for the $cert object. |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
=cut |
1316
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
sub tune_cert { |
1318
|
27
|
|
|
27
|
1
|
79
|
my ($self,$cert,$method)=@_; |
1319
|
27
|
50
|
|
|
|
171
|
return new_false Data::Result("Unsupported hashing method: $method") unless exists $self->tune_cert_cbs->{$method}; |
1320
|
|
|
|
|
|
|
|
1321
|
27
|
|
|
|
|
152
|
$self->tune_cert_cbs->{$method}->($self,$cert); |
1322
|
27
|
|
|
|
|
95
|
return new_true Data::Result; |
1323
|
|
|
|
|
|
|
} |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
=head2 my $x509=$self->clean_x509($string) |
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
Converts a given string to an x509 certificate. |
1328
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
=cut |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
sub clean_x509 { |
1332
|
17
|
|
|
17
|
1
|
55
|
my ($self,$cert)=@_; |
1333
|
17
|
|
|
|
|
473
|
$cert =~ s/\s+//g; |
1334
|
17
|
|
|
|
|
32
|
my @lines; |
1335
|
17
|
|
|
|
|
201
|
while (length $cert > 64) { |
1336
|
285
|
|
|
|
|
1813
|
push @lines, substr $cert, 0, 64, ''; |
1337
|
|
|
|
|
|
|
} |
1338
|
17
|
|
|
|
|
41
|
push @lines,$cert; |
1339
|
17
|
|
|
|
|
125
|
$cert = join "\n", @lines; |
1340
|
17
|
|
|
|
|
83
|
$cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "\n-----END CERTIFICATE-----\n"; |
1341
|
17
|
|
|
|
|
89
|
return $cert; |
1342
|
|
|
|
|
|
|
} |
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
=head2 my $result=$self->transform($xpath_object,$node,$transformType,$nth,$ec14n_inclusive_prefixes) |
1345
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
Given the $node XML::LibXML::Element and $transformType, returns a Data::Result object. When true the call to $result->get_data will return the xml, when false it will contain a string that shows why it failed. |
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
=cut |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
sub transform { |
1351
|
79
|
|
|
79
|
1
|
1655
|
my ($self,$x,$node,$type,$nth,$ec14n_inclusive_prefixes)=@_; |
1352
|
79
|
50
|
|
|
|
424
|
return new_false Data::Result("tansform of [$type] is not supported") unless exists $self->mutate_cbs->{$type}; |
1353
|
79
|
|
|
|
|
490
|
return new_true Data::Result($self->mutate_cbs->{$type}->($self,$x,$node,$nth,$ec14n_inclusive_prefixes)); |
1354
|
|
|
|
|
|
|
} |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
=head2 my $array_ref=$self->transforms |
1357
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
Returns an ArrayRef that contains the list of transform methods we will use when signing the xml. |
1359
|
|
|
|
|
|
|
|
1360
|
|
|
|
|
|
|
This list is built out of the following: |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
0: $self->envelope_method |
1363
|
|
|
|
|
|
|
1: $self->canon_method |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
=cut |
1366
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
sub transforms { |
1368
|
12
|
|
|
12
|
1
|
28
|
my ($self)=@_; |
1369
|
12
|
|
|
|
|
65
|
return [$self->envelope_method,$self->canon_method]; |
1370
|
|
|
|
|
|
|
} |
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
=head2 my $xml=$self->create_digest_xml($id,$digest) |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
Produces a text xml fragment to be used for an xml digest. |
1375
|
|
|
|
|
|
|
|
1376
|
|
|
|
|
|
|
=cut |
1377
|
|
|
|
|
|
|
|
1378
|
|
|
|
|
|
|
sub create_digest_xml { |
1379
|
6
|
|
|
6
|
1
|
16
|
my ($self,$id,$digest)=@_; |
1380
|
6
|
|
|
|
|
14
|
my $method=$self->digest_method; |
1381
|
6
|
|
|
|
|
10
|
my @list; |
1382
|
6
|
|
|
|
|
24
|
my $ns=$self->tag_namespace; |
1383
|
6
|
|
|
|
|
24
|
my $transforms=$self->transforms; |
1384
|
6
|
|
|
|
|
16
|
foreach my $transform (@{$transforms}) { |
|
6
|
|
|
|
|
21
|
|
1385
|
12
|
|
|
|
|
50
|
push @list, |
1386
|
|
|
|
|
|
|
qq{ <${ns}:Transform Algorithm="$transform" />}; |
1387
|
|
|
|
|
|
|
} |
1388
|
6
|
|
|
|
|
24
|
$transforms=join "\n",@list; |
1389
|
6
|
|
|
|
|
98
|
return qq{<${ns}:Reference URI="#$id"> |
1390
|
|
|
|
|
|
|
<${ns}:Transforms>\n$transforms |
1391
|
|
|
|
|
|
|
</${ns}:Transforms> |
1392
|
|
|
|
|
|
|
<${ns}:DigestMethod Algorithm="$method" /> |
1393
|
|
|
|
|
|
|
<${ns}:DigestValue>$digest</${ns}:DigestValue> |
1394
|
|
|
|
|
|
|
</${ns}:Reference>}; |
1395
|
|
|
|
|
|
|
} |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
=head2 my $xml=$self->create_signedinfo_xml($digest_xml) |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
Produces text xml fragment to be used for an xml signature |
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
=cut |
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
sub create_signedinfo_xml { |
1404
|
6
|
|
|
6
|
1
|
14
|
my ($self,$digest_xml) = @_; |
1405
|
6
|
|
|
|
|
27
|
my $method=$self->signature_method; |
1406
|
6
|
|
|
|
|
19
|
my $canon_method=$self->canon_method; |
1407
|
6
|
|
|
|
|
18
|
my $xmlns=$self->create_xmlns; |
1408
|
6
|
|
|
|
|
16
|
my $ns=$self->tag_namespace; |
1409
|
6
|
|
|
|
|
62
|
return qq{<${ns}:SignedInfo $xmlns> |
1410
|
|
|
|
|
|
|
<${ns}:CanonicalizationMethod Algorithm="$canon_method" /> |
1411
|
|
|
|
|
|
|
<${ns}:SignatureMethod Algorithm="$method" /> |
1412
|
|
|
|
|
|
|
$digest_xml |
1413
|
|
|
|
|
|
|
</${ns}:SignedInfo>}; |
1414
|
|
|
|
|
|
|
} |
1415
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
=head2 my $xmlns=$self->create_xmlns |
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
Creates our common xmlns string based on our namespaces. |
1419
|
|
|
|
|
|
|
|
1420
|
|
|
|
|
|
|
=cut |
1421
|
|
|
|
|
|
|
|
1422
|
|
|
|
|
|
|
sub create_xmlns { |
1423
|
12
|
|
|
12
|
1
|
29
|
my ($self)=@_; |
1424
|
12
|
|
|
|
|
25
|
my @list; |
1425
|
12
|
|
|
|
|
21
|
foreach my $key (sort keys %{$self->namespaces}) { |
|
12
|
|
|
|
|
139
|
|
1426
|
36
|
|
|
|
|
81
|
my $value=$self->namespaces->{$key}; |
1427
|
36
|
|
|
|
|
117
|
push @list,qq{xmlns:${key}="$value"}; |
1428
|
|
|
|
|
|
|
} |
1429
|
|
|
|
|
|
|
|
1430
|
12
|
|
|
|
|
51
|
my $xmlns=join ' ',@list; |
1431
|
12
|
|
|
|
|
34
|
return $xmlns; |
1432
|
|
|
|
|
|
|
} |
1433
|
|
|
|
|
|
|
|
1434
|
|
|
|
|
|
|
=head2 my $xml=$self->create_signature_xml |
1435
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
Creates the signature xml for signing. |
1437
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
=cut |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
sub create_signature_xml { |
1441
|
6
|
|
|
6
|
1
|
32
|
my ($self,$signed_info,$signature_value,$key_string)=@_; |
1442
|
6
|
|
|
|
|
26
|
my $xmlns=$self->create_xmlns; |
1443
|
6
|
|
|
|
|
25
|
my $ns=$self->tag_namespace; |
1444
|
6
|
|
|
|
|
94
|
return qq{<${ns}:Signature $xmlns> |
1445
|
|
|
|
|
|
|
$signed_info |
1446
|
|
|
|
|
|
|
<${ns}:SignatureValue>$signature_value</${ns}:SignatureValue> |
1447
|
|
|
|
|
|
|
$key_string |
1448
|
|
|
|
|
|
|
</${ns}:Signature>}; |
1449
|
|
|
|
|
|
|
} |
1450
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
=head2 my $result=$self->load_cert_from_file($filename) |
1452
|
|
|
|
|
|
|
|
1453
|
|
|
|
|
|
|
Returns a Data::Result structure, when true it contains a hasref with the following elements: |
1454
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
type: 'dsa|rsa|x509' |
1456
|
|
|
|
|
|
|
cert: $cert_object |
1457
|
|
|
|
|
|
|
|
1458
|
|
|
|
|
|
|
=cut |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
sub load_cert_from_file { |
1461
|
6
|
|
|
6
|
1
|
22
|
my ($self,$file)=@_; |
1462
|
6
|
50
|
|
|
|
30
|
return new_false Data::Result("file is not defined") unless defined($file); |
1463
|
6
|
50
|
|
|
|
290
|
return new_false Data::Result("cannot read: $file") unless -r $file; |
1464
|
|
|
|
|
|
|
|
1465
|
6
|
|
|
|
|
83
|
my $io=IO::File->new($file,'r'); |
1466
|
6
|
50
|
|
|
|
1043
|
return new_false Data::Result("Cannot open $file, error was $!") unless $io; |
1467
|
6
|
|
|
|
|
188
|
my $text=join '',$io->getlines; |
1468
|
6
|
|
|
|
|
709
|
return $self->detect_cert($text); |
1469
|
|
|
|
|
|
|
} |
1470
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
=head2 my $result=$self->detect_cert($text) |
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the following hashref |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
type: 'dsa|rsa|x509' |
1476
|
|
|
|
|
|
|
cert: $cert_object |
1477
|
|
|
|
|
|
|
|
1478
|
|
|
|
|
|
|
=cut |
1479
|
|
|
|
|
|
|
|
1480
|
|
|
|
|
|
|
sub detect_cert { |
1481
|
6
|
|
|
6
|
1
|
22
|
my ($self,$text)=@_; |
1482
|
6
|
100
|
|
|
|
62
|
if ($text =~ m/BEGIN ([DR]SA) PRIVATE KEY/s ) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
|
1484
|
5
|
100
|
|
|
|
29
|
if($1 eq 'RSA') { |
1485
|
4
|
|
|
|
|
18
|
return $self->load_rsa_string($text); |
1486
|
|
|
|
|
|
|
} else { |
1487
|
1
|
|
|
|
|
6
|
return $self->load_dsa_string($text); |
1488
|
|
|
|
|
|
|
} |
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
} elsif ( $text =~ m/BEGIN PRIVATE KEY/ ) { |
1491
|
0
|
|
|
|
|
0
|
return $self->load_rsa_string($text); |
1492
|
|
|
|
|
|
|
} elsif ($text =~ m/BEGIN CERTIFICATE/) { |
1493
|
1
|
|
|
|
|
8
|
return $self->load_x509_string($text); |
1494
|
|
|
|
|
|
|
} else { |
1495
|
0
|
|
|
|
|
0
|
return new_false Data::Result("Unsupported key type"); |
1496
|
|
|
|
|
|
|
} |
1497
|
|
|
|
|
|
|
} |
1498
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
=head2 my $result=$self->load_rsa_string($string) |
1500
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the following hashref: |
1502
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
type: 'rsa' |
1504
|
|
|
|
|
|
|
cert: $cert_object |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
=cut |
1507
|
|
|
|
|
|
|
|
1508
|
|
|
|
|
|
|
sub load_rsa_string { |
1509
|
4
|
|
|
4
|
1
|
16
|
my ($self,$str)=@_; |
1510
|
4
|
|
|
|
|
361
|
my $rsaKey = Crypt::OpenSSL::RSA->new_private_key( $str ); |
1511
|
4
|
50
|
|
|
|
23
|
return new_false Data::Result("Failed to parse rsa key") unless $rsaKey; |
1512
|
4
|
|
|
|
|
26
|
$rsaKey->use_pkcs1_padding(); |
1513
|
4
|
|
|
|
|
46
|
return new_true Data::Result({cert=>$rsaKey,type=>'rsa'}); |
1514
|
|
|
|
|
|
|
} |
1515
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
=head2 my $result=$self->load_x509_string($string) |
1517
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the following hashref: |
1519
|
|
|
|
|
|
|
|
1520
|
|
|
|
|
|
|
type: 'x509' |
1521
|
|
|
|
|
|
|
cert: $cert_object |
1522
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
=cut |
1524
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
sub load_x509_string { |
1526
|
1
|
|
|
1
|
1
|
4
|
my ($self,$str)=@_; |
1527
|
1
|
|
|
|
|
128
|
my $x509Key = Crypt::OpenSSL::X509->new_from_string( $str ); |
1528
|
1
|
50
|
|
|
|
19
|
return new_false Data::Result("Failed to parse x509 cert") unless $x509Key; |
1529
|
1
|
|
|
|
|
15
|
return new_true Data::Result({cert=>$x509Key,type=>'x509'}); |
1530
|
|
|
|
|
|
|
} |
1531
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
=head2 my $result=$self->load_dsa_string($string) |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the following hashref: |
1535
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
type: 'dsa' |
1537
|
|
|
|
|
|
|
cert: $cert_object |
1538
|
|
|
|
|
|
|
|
1539
|
|
|
|
|
|
|
=cut |
1540
|
|
|
|
|
|
|
|
1541
|
|
|
|
|
|
|
sub load_dsa_string { |
1542
|
1
|
|
|
1
|
1
|
4
|
my ($self,$str)=@_; |
1543
|
1
|
|
|
|
|
13
|
my $dsa_key = Crypt::OpenSSL::DSA->read_priv_key_str( $str ); |
1544
|
1
|
50
|
|
|
|
150
|
return new_false("Failed to parse dsa key") unless $dsa_key; |
1545
|
1
|
|
|
|
|
14
|
return new_true Data::Result({cert=>$dsa_key,type=>'dsa'}); |
1546
|
|
|
|
|
|
|
} |
1547
|
|
|
|
|
|
|
|
1548
|
|
|
|
|
|
|
=head2 my $result=$self->get_xml_to_sign($xpath_object,$nth) |
1549
|
|
|
|
|
|
|
|
1550
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the xml object to sign. |
1551
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
=cut |
1553
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
sub get_xml_to_sign { |
1555
|
7
|
|
|
7
|
1
|
24
|
my ($self,$x,$nth)=@_; |
1556
|
7
|
|
|
|
|
43
|
my $xpath=$self->context($self->xpath_ToSign,$nth); |
1557
|
7
|
|
|
|
|
35
|
my ($node)=$x->findnodes($xpath); |
1558
|
|
|
|
|
|
|
|
1559
|
7
|
50
|
|
|
|
371
|
return new_false Data::Result("Failed to find xml to sign in xpath: $xpath") unless defined($node); |
1560
|
7
|
|
|
|
|
28
|
return new_true Data::Result($node); |
1561
|
|
|
|
|
|
|
} |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
=head2 my $result=$self->get_signer_id($xpath_object,$nth) |
1564
|
|
|
|
|
|
|
|
1565
|
|
|
|
|
|
|
Returns a Data::Result object, when true it contains the id value |
1566
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
=cut |
1568
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
sub get_signer_id { |
1570
|
6
|
|
|
6
|
1
|
19
|
my ($self,$x,$nth)=@_; |
1571
|
6
|
|
|
|
|
45
|
my $xpath=$self->context($self->xpath_IdValue,$nth); |
1572
|
6
|
|
|
|
|
31
|
my ($node)=$x->findvalue($xpath); |
1573
|
6
|
50
|
|
|
|
593
|
return new_false Data::Result("Failed to find id value in xpath: $xpath") unless defined($node); |
1574
|
6
|
|
|
|
|
24
|
return new_true Data::Result($node); |
1575
|
|
|
|
|
|
|
} |
1576
|
|
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
=head2 my $result=$self->sign |
1578
|
|
|
|
|
|
|
|
1579
|
|
|
|
|
|
|
Returns a Data::Result Object, when true it contains the signed xml string. |
1580
|
|
|
|
|
|
|
|
1581
|
|
|
|
|
|
|
=cut |
1582
|
|
|
|
|
|
|
|
1583
|
|
|
|
|
|
|
sub sign { |
1584
|
5
|
|
|
5
|
1
|
1843
|
my ($self)=@_; |
1585
|
5
|
|
|
|
|
20
|
my $x=$self->build_xpath; |
1586
|
|
|
|
|
|
|
|
1587
|
5
|
50
|
|
|
|
129
|
return new_false Data::Result("sign_cert object is not defined") unless defined($self->sign_cert); |
1588
|
|
|
|
|
|
|
|
1589
|
5
|
|
|
|
|
72
|
my $total=$x->findnodes($self->xpath_ToSign)->size; |
1590
|
5
|
50
|
|
|
|
458
|
return new_false Data::Result("No xml found to sign") if $total==0; |
1591
|
5
|
|
|
|
|
144
|
foreach(my $nth=1;$nth <=$total;++$nth) { |
1592
|
6
|
|
|
|
|
152
|
my $result=$self->sign_chunk($x,$nth); |
1593
|
6
|
50
|
|
|
|
1959
|
return $result unless $result; |
1594
|
|
|
|
|
|
|
} |
1595
|
5
|
|
|
|
|
700
|
my ($root)=$x->findnodes($self->xpath_Root); |
1596
|
|
|
|
|
|
|
|
1597
|
5
|
|
|
|
|
329
|
return new_true Data::Result($root->toString); |
1598
|
|
|
|
|
|
|
} |
1599
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
=head2 my $result=$self->sign_chunk($xpath_object,$nth) |
1601
|
|
|
|
|
|
|
|
1602
|
|
|
|
|
|
|
Returns a Data::Result object, when true, the nth element with //@ID was signed and updated in $xpath_object. This method provides absolute granular control over what node is signed. |
1603
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
=cut |
1605
|
|
|
|
|
|
|
|
1606
|
|
|
|
|
|
|
sub sign_chunk { |
1607
|
6
|
|
|
6
|
1
|
22
|
my ($self,$x,$nth)=@_; |
1608
|
|
|
|
|
|
|
|
1609
|
6
|
|
|
|
|
26
|
my $result=$self->get_xml_to_sign($x,$nth); |
1610
|
6
|
50
|
|
|
|
1191
|
return $result unless $result; |
1611
|
6
|
|
|
|
|
302
|
my $node_to_sign=$result->get_data; |
1612
|
|
|
|
|
|
|
|
1613
|
6
|
|
|
|
|
47
|
$result=$self->get_signer_id($x,$nth); |
1614
|
6
|
50
|
|
|
|
1775
|
return $result unless $result; |
1615
|
6
|
|
|
|
|
296
|
my $id=$result->get_data; |
1616
|
|
|
|
|
|
|
|
1617
|
6
|
|
|
|
|
75
|
my $digest_canon=$self->mutate_cbs->{$self->canon_method}->($self,$x,$node_to_sign,$nth,undef); |
1618
|
6
|
|
|
|
|
580
|
my $digest=$self->digest_cbs->{$self->digest_method}->($self,$digest_canon); |
1619
|
|
|
|
|
|
|
|
1620
|
6
|
|
|
|
|
30
|
my $digest_xml = $self->create_digest_xml( $id,$digest ); |
1621
|
6
|
|
|
|
|
28
|
my $signedinfo_xml = $self->create_signedinfo_xml($digest_xml); |
1622
|
6
|
|
|
|
|
27
|
my $p= XML::LibXML->new(); |
1623
|
|
|
|
|
|
|
|
1624
|
|
|
|
|
|
|
# fun note, we have to append the child to get it to canonize correctly |
1625
|
6
|
|
|
|
|
108
|
my $signed_info=$p->parse_balanced_chunk($signedinfo_xml); |
1626
|
6
|
|
|
|
|
1544
|
$node_to_sign->appendChild($signed_info); |
1627
|
6
|
|
|
|
|
45
|
$result=$self->get_signed_info_node($x,$nth); |
1628
|
6
|
50
|
|
|
|
1763
|
return $result unless $result; |
1629
|
6
|
|
|
|
|
327
|
$signed_info=$result->get_data; |
1630
|
|
|
|
|
|
|
|
1631
|
6
|
|
|
|
|
47
|
my $canon; |
1632
|
6
|
|
|
|
|
13
|
foreach my $method (@{$self->transforms}) { |
|
6
|
|
|
|
|
15
|
|
1633
|
12
|
|
|
|
|
57
|
$result=$self->transform($x,$signed_info,$method,$nth,undef); |
1634
|
12
|
50
|
|
|
|
4057
|
return $result unless $result; |
1635
|
12
|
|
|
|
|
577
|
$canon=$result->get_data; |
1636
|
|
|
|
|
|
|
} |
1637
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
# now we need to remove the child to contnue on |
1639
|
6
|
|
|
|
|
103
|
$node_to_sign->removeChild($signed_info); |
1640
|
|
|
|
|
|
|
|
1641
|
6
|
|
|
|
|
30
|
my $sig; |
1642
|
6
|
|
|
|
|
316
|
my $cert=$self->sign_cert; |
1643
|
6
|
100
|
|
|
|
209
|
if ($self->key_type eq 'dsa') { |
|
|
50
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
# DSA only permits the signing of 20 bytes or less, hence the sha1 |
1645
|
1
|
|
|
|
|
296
|
my $raw= $cert->sign( sha1($canon) ); |
1646
|
1
|
|
|
|
|
8
|
$sig=encode_base64( $raw, "\n" ); |
1647
|
|
|
|
|
|
|
} elsif($self->key_type eq 'rsa') { |
1648
|
5
|
|
|
|
|
169
|
my $result=$self->tune_cert($cert,$self->signature_method); |
1649
|
5
|
50
|
|
|
|
857
|
return $result unless $result; |
1650
|
5
|
|
|
|
|
62645
|
my $raw= $cert->sign( $canon ); |
1651
|
5
|
|
|
|
|
483
|
$sig=encode_base64( $raw, "\n" ); |
1652
|
|
|
|
|
|
|
} |
1653
|
6
|
|
|
|
|
719
|
my $method="create_".$self->key_type."_xml"; |
1654
|
6
|
|
|
|
|
110
|
my $key_xml=$self->$method($cert); |
1655
|
6
|
|
|
|
|
774
|
my $signed_xml=$self->create_signature_xml($signed_info->toString,$sig,$key_xml); |
1656
|
6
|
|
|
|
|
34
|
my $signed_frag=$p->parse_balanced_chunk($signed_xml); |
1657
|
6
|
|
|
|
|
2219
|
$node_to_sign->appendChild($signed_frag); |
1658
|
6
|
|
|
|
|
46
|
return new_true Data::Result("OK"); |
1659
|
|
|
|
|
|
|
} |
1660
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
=head2 my $xml=$self->create_x509_xml($cert) |
1662
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
Creates the xml from the Certificate Object. |
1664
|
|
|
|
|
|
|
|
1665
|
|
|
|
|
|
|
=cut |
1666
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
sub create_x509_xml { |
1668
|
0
|
|
|
0
|
1
|
0
|
my ($self,$cert)=@_; |
1669
|
0
|
|
|
|
|
0
|
my $cert_text = $cert->as_string; |
1670
|
0
|
|
|
|
|
0
|
return $self->build_x509_xml($cert_text); |
1671
|
|
|
|
|
|
|
} |
1672
|
|
|
|
|
|
|
|
1673
|
|
|
|
|
|
|
=head2 my $xml=$self->build_x509_xml($encoded_key) |
1674
|
|
|
|
|
|
|
|
1675
|
|
|
|
|
|
|
Given the base64 encoded key, create a block of x509 xml. |
1676
|
|
|
|
|
|
|
|
1677
|
|
|
|
|
|
|
=cut |
1678
|
|
|
|
|
|
|
|
1679
|
|
|
|
|
|
|
sub build_x509_xml { |
1680
|
1
|
|
|
1
|
1
|
4
|
my ($self,$cert_text)=@_; |
1681
|
1
|
|
|
|
|
6
|
my $ns=$self->tag_namespace; |
1682
|
1
|
|
|
|
|
12
|
$cert_text =~ s/-----[^-]*-----//gm; |
1683
|
1
|
|
|
|
|
6
|
return "<${ns}:KeyInfo><${ns}:X509Data><${ns}:X509Certificate>\n"._trim($cert_text)."\n</${ns}:X509Certificate></${ns}:X509Data></${ns}:KeyInfo>"; |
1684
|
|
|
|
|
|
|
} |
1685
|
|
|
|
|
|
|
|
1686
|
|
|
|
|
|
|
=head2 my $result=$self->find_key_cert |
1687
|
|
|
|
|
|
|
|
1688
|
|
|
|
|
|
|
Returns a Data::Result Object, when true it contains the x509 cert xml. |
1689
|
|
|
|
|
|
|
|
1690
|
|
|
|
|
|
|
=cut |
1691
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
sub find_key_cert { |
1693
|
5
|
|
|
5
|
1
|
18
|
my ($self)=@_; |
1694
|
5
|
100
|
|
|
|
96
|
if(defined(my $file=$self->cert_file)) { |
|
|
50
|
|
|
|
|
|
1695
|
1
|
|
|
|
|
16
|
my $result=$self->load_cert_from_file($file); |
1696
|
1
|
50
|
|
|
|
283
|
if($result) { |
1697
|
1
|
|
|
|
|
63
|
my $str=_trim($result->get_data->{cert}->as_string); |
1698
|
1
|
|
|
|
|
7
|
return new_true Data::Result($self->build_x509_xml($str)); |
1699
|
|
|
|
|
|
|
} else { |
1700
|
0
|
|
|
|
|
0
|
return $result; |
1701
|
|
|
|
|
|
|
} |
1702
|
|
|
|
|
|
|
} elsif(defined($self->cert_string)) { |
1703
|
0
|
|
|
|
|
0
|
return new_true Data::Result($self->build_x509_xml(_trim($self->cert_string))); |
1704
|
|
|
|
|
|
|
} |
1705
|
|
|
|
|
|
|
|
1706
|
4
|
|
|
|
|
77
|
return new_false Data::Result("no cert found"); |
1707
|
|
|
|
|
|
|
} |
1708
|
|
|
|
|
|
|
|
1709
|
|
|
|
|
|
|
=head2 my $xml=$self->create_rsa_xml($cert) |
1710
|
|
|
|
|
|
|
|
1711
|
|
|
|
|
|
|
Creates the xml from the Certificate Object. |
1712
|
|
|
|
|
|
|
|
1713
|
|
|
|
|
|
|
=cut |
1714
|
|
|
|
|
|
|
|
1715
|
|
|
|
|
|
|
sub create_rsa_xml { |
1716
|
5
|
|
|
5
|
1
|
19
|
my ($self,$rsaKey)=@_; |
1717
|
|
|
|
|
|
|
|
1718
|
5
|
|
|
|
|
25
|
my $result=$self->find_key_cert; |
1719
|
5
|
100
|
|
|
|
1437
|
return $result->get_data if $result; |
1720
|
|
|
|
|
|
|
|
1721
|
4
|
|
|
|
|
319
|
my $bigNum = ( $rsaKey->get_key_parameters() )[1]; |
1722
|
4
|
|
|
|
|
684
|
my $bin = $bigNum->to_bin(); |
1723
|
4
|
|
|
|
|
17
|
my $exp = encode_base64( $bin, '' ); |
1724
|
4
|
|
|
|
|
102
|
$bigNum = ( $rsaKey->get_key_parameters() )[0]; |
1725
|
4
|
|
|
|
|
124
|
$bin = $bigNum->to_bin(); |
1726
|
4
|
|
|
|
|
18
|
my $mod = encode_base64( $bin, '' ); |
1727
|
4
|
|
|
|
|
20
|
my $ns=$self->tag_namespace; |
1728
|
|
|
|
|
|
|
|
1729
|
4
|
|
|
|
|
111
|
return "<${ns}:KeyInfo> |
1730
|
|
|
|
|
|
|
<${ns}:KeyValue> |
1731
|
|
|
|
|
|
|
<${ns}:RSAKeyValue> |
1732
|
|
|
|
|
|
|
<${ns}:Modulus>$mod</${ns}:Modulus> |
1733
|
|
|
|
|
|
|
<${ns}:Exponent>$exp</${ns}:Exponent> |
1734
|
|
|
|
|
|
|
</${ns}:RSAKeyValue> |
1735
|
|
|
|
|
|
|
</${ns}:KeyValue> |
1736
|
|
|
|
|
|
|
</${ns}:KeyInfo>"; |
1737
|
|
|
|
|
|
|
} |
1738
|
|
|
|
|
|
|
|
1739
|
|
|
|
|
|
|
=head2 my $xml=$self->create_dsa_xml($cert) |
1740
|
|
|
|
|
|
|
|
1741
|
|
|
|
|
|
|
Creates the xml for the Key Object. |
1742
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
=cut |
1744
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
sub create_dsa_xml { |
1746
|
1
|
|
|
1
|
1
|
4
|
my ($self,$dsa_key)=@_; |
1747
|
|
|
|
|
|
|
|
1748
|
1
|
|
|
|
|
9
|
my $g=encode_base64( $dsa_key->get_g(), '' ); |
1749
|
1
|
|
|
|
|
8
|
my $p=encode_base64( $dsa_key->get_p(), '' ); |
1750
|
1
|
|
|
|
|
7
|
my $q=encode_base64( $dsa_key->get_q(), '' ); |
1751
|
1
|
|
|
|
|
7
|
my $y=encode_base64( $dsa_key->get_pub_key(), '' ); |
1752
|
|
|
|
|
|
|
|
1753
|
1
|
|
|
|
|
4
|
my $ns=$self->tag_namespace; |
1754
|
1
|
|
|
|
|
16
|
return "<${ns}:KeyInfo> |
1755
|
|
|
|
|
|
|
<${ns}:KeyValue> |
1756
|
|
|
|
|
|
|
<${ns}:DSAKeyValue> |
1757
|
|
|
|
|
|
|
<${ns}:P>$p</${ns}:P> |
1758
|
|
|
|
|
|
|
<${ns}:Q>$q</${ns}:Q> |
1759
|
|
|
|
|
|
|
<${ns}:G>$g</${ns}:G> |
1760
|
|
|
|
|
|
|
<${ns}:Y>$y</${ns}:Y> |
1761
|
|
|
|
|
|
|
</${ns}:DSAKeyValue> |
1762
|
|
|
|
|
|
|
</${ns}:KeyValue> |
1763
|
|
|
|
|
|
|
</${ns}:KeyInfo>"; |
1764
|
|
|
|
|
|
|
} |
1765
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
sub _trim { |
1767
|
14
|
|
|
14
|
|
610
|
my ($str)=@_; |
1768
|
14
|
|
|
|
|
880
|
$str=~ s/(?:^\s+|\s+$)//sg; |
1769
|
14
|
|
|
|
|
58
|
return $str; |
1770
|
|
|
|
|
|
|
} |
1771
|
|
|
|
|
|
|
|
1772
|
|
|
|
|
|
|
=head1 Limitations |
1773
|
|
|
|
|
|
|
|
1774
|
|
|
|
|
|
|
This package currently has some limitations. |
1775
|
|
|
|
|
|
|
|
1776
|
|
|
|
|
|
|
=head2 Supported Key Types and formats for signing/validation |
1777
|
|
|
|
|
|
|
|
1778
|
|
|
|
|
|
|
Currently this module only supports RSA and DSA keys in pem format. |
1779
|
|
|
|
|
|
|
|
1780
|
|
|
|
|
|
|
=head2 CaCert Validation |
1781
|
|
|
|
|
|
|
|
1782
|
|
|
|
|
|
|
Currently CaCert validation only works with RSA keys. |
1783
|
|
|
|
|
|
|
|
1784
|
|
|
|
|
|
|
=head1 Credits |
1785
|
|
|
|
|
|
|
|
1786
|
|
|
|
|
|
|
This code is based on the following modules: L<XML::Sig>, L<Net::SAML2::XML::Sig>, L<Authen::NZRealMe::XMLSig>, and L<Mojo::XMLSig> and would not exist today withot them. |
1787
|
|
|
|
|
|
|
|
1788
|
|
|
|
|
|
|
=head1 Bugs |
1789
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
Currently there are no known bugs, but if any are found please report them on our github project. Patches and pull requests are welcomed! |
1791
|
|
|
|
|
|
|
|
1792
|
|
|
|
|
|
|
L<https://github.com/akalinux/xml-sig-oo> |
1793
|
|
|
|
|
|
|
|
1794
|
|
|
|
|
|
|
=head1 Author |
1795
|
|
|
|
|
|
|
|
1796
|
|
|
|
|
|
|
AKALINUX <AKALINUX@CPAN.ORG> |
1797
|
|
|
|
|
|
|
|
1798
|
|
|
|
|
|
|
=cut |
1799
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
1; |