File Coverage

blib/lib/XML/Sig.pm
Criterion Covered Total %
statement 594 661 89.8
branch 187 300 62.3
condition 30 42 71.4
subroutine 55 59 93.2
pod 4 4 100.0
total 870 1066 81.6


line stmt bran cond sub pod time code
1 23     23   2955187 use strict;
  23         255  
  23         675  
2 23     23   120 use warnings;
  23         47  
  23         1194  
3              
4             package XML::Sig;
5             our $VERSION = '0.63';
6              
7 23     23   11401 use Encode;
  23         310471  
  23         1982  
8             # ABSTRACT: XML::Sig - A toolkit to help sign and verify XML Digital Signatures
9              
10             # use 'our' on v5.6.0
11 23     23   180 use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG);
  23         56  
  23         1617  
12              
13             $DEBUG = 0;
14              
15 23     23   159 use base qw(Class::Accessor);
  23         73  
  23         13196  
16             XML::Sig->mk_accessors(qw(key));
17              
18              
19 23     23   76348 use Digest::SHA qw(sha1 sha224 sha256 sha384 sha512 hmac_sha1 hmac_sha256 hmac_sha384 hmac_sha512);
  23         72504  
  23         2582  
20 23     23   11542 use Crypt::Digest::RIPEMD160 qw/ripemd160/;
  23         101692  
  23         1314  
21 23     23   16954 use XML::LibXML;
  23         938315  
  23         158  
22 23     23   11466 use MIME::Base64;
  23         10449  
  23         1355  
23 23     23   187 use Carp;
  23         49  
  23         1155  
24              
25              
26 23     23   152 use constant TRANSFORM_ENV_SIG => 'http://www.w3.org/2000/09/xmldsig#enveloped-signature';
  23         48  
  23         1460  
27 23     23   164 use constant TRANSFORM_C14N => 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
  23         49  
  23         1162  
28 23     23   176 use constant TRANSFORM_C14N_COMMENTS => 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments';
  23         65  
  23         1107  
29 23     23   131 use constant TRANSFORM_C14N_V1_1 => 'http://www.w3.org/TR/2008/REC-xml-c14n11-20080502';
  23         57  
  23         1133  
30 23     23   167 use constant TRANSFORM_C14N_V1_1_COMMENTS => 'http://www.w3.org/TR/2008/REC-xml-c14n11-20080502#WithComments';
  23         50  
  23         1340  
31 23     23   165 use constant TRANSFORM_EXC_C14N => 'http://www.w3.org/2001/10/xml-exc-c14n#';
  23         53  
  23         1252  
32 23     23   156 use constant TRANSFORM_EXC_C14N_COMMENTS => 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments';
  23         52  
  23         106743  
33              
34       0     sub DESTROY { }
35              
36             $| = 1; # autoflush
37              
38              
39              
40              
41             sub new {
42 681     681 1 648790 my $class = shift;
43 681         1285 my $params = shift;
44 681         1130 my $self = {};
45 681         1445 foreach my $prop ( qw/ key cert cert_text ns id_attr/ ) {
46 3405 100       6974 if ( exists $params->{ $prop } ) {
47 448         1021 $self->{ $prop } = $params->{ $prop };
48             }
49             # else {
50             # confess "You need to provide the $prop parameter!";
51             # }
52             }
53 681         1309 bless $self, $class;
54 681 100       2293 $self->{ 'x509' } = exists $params->{ x509 } ? 1 : 0;
55 681 100       1466 if ( exists $params->{ key_name } ) {
56 18         36 $self->{ key_name } = $params->{ key_name };
57             }
58 681 100       1477 if ( exists $params->{ 'key' } ) {
59 337         1100 $self->_load_key( $params->{ 'key' } );
60             }
61 679 100       1664 if ( exists $params->{ 'cert' } ) {
62 108         434 $self->_load_cert_file( $params->{ 'cert' } );
63             }
64 679 100       1511 if ( exists $params->{ 'cert_text' } ) {
65 1         3 $self->_load_cert_text( $params->{ 'cert_text' } );
66             }
67 679 100       1454 if ( exists $params->{ 'hmac_key' } ) {
68 18         55 $self->_load_hmac_key_info;
69             }
70              
71 679 100 100     1972 if ( exists $params->{ sig_hash } && grep { $_ eq $params->{ sig_hash } } ('sha224', 'sha256', 'sha384', 'sha512', 'ripemd160'))
  910         2172  
72             {
73 151         400 $self->{ sig_hash } = $params->{ sig_hash };
74             }
75             else {
76 528         966 $self->{ sig_hash } = 'sha256';
77             }
78              
79 679 100 66     1879 if ( exists $params->{ digest_hash } && grep { $_ eq $params->{ digest_hash } } ('sha1', 'sha224', 'sha256', 'sha384','sha512', 'ripemd160'))
  1224         2301  
80             {
81 204         441 $self->{ digest_hash } = $params->{ digest_hash };
82             }
83             else {
84 475         807 $self->{ digest_hash } = 'sha256';
85             }
86              
87 679 100 100     2382 if (defined $self->{ key_type } && $self->{ key_type } eq 'dsa') {
88 56         238 my $sig_size = $self->{ key_obj }->get_sig_size();
89              
90             # The key size dictates the sig size
91 56 100       170 if ( $sig_size eq 48 ) { # 1024-bit key
92 28         61 $self->{ sig_hash } = 'sha1';
93             } else { # 2048-bit or 3072-bit key
94 28         50 $self->{ sig_hash } = 'sha256';
95             }
96             }
97              
98 679 100 100     1677 if ( exists $params->{ no_xml_declaration } && $params->{ no_xml_declaration } == 1 ) {
99 1         4 $self->{ no_xml_declaration } = 1;
100             } else {
101 678         1368 $self->{ no_xml_declaration } = 0;
102             }
103              
104 679 100 100     2457 if ( !defined $self->{ key_type } && exists $params->{ hmac_key } ) {
105 18         34 $self->{ hmac_key } = $params->{ hmac_key };
106 18         40 $self->{ key_type } = 'hmac';
107             }
108              
109 679         13437 return $self;
110             }
111              
112              
113             sub sign {
114 338     338 1 163991 my $self = shift;
115 338         812 my ($xml) = @_;
116              
117 338 100 100     1223 die "You cannot sign XML without a private key." unless $self->key || $self->{ hmac_key };
118              
119 337         5126 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
120              
121 337         2047 my $dom = XML::LibXML->load_xml( string => $xml );
122              
123 337         94838 $self->{ parser } = XML::LibXML::XPathContext->new($dom);
124 337         2247 $self->{ parser }->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
125 337         1182 $self->{ parser }->registerNs('ec', 'http://www.w3.org/2001/10/xml-exc-c14n#');
126 337         1159 $self->{ parser }->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
127 337 100       892 if ($self->{ns}) {
128 1         2 foreach (keys %{$self->{ns}}) {
  1         31  
129 1         6 $self->{ parser }->registerNs($_, $self->{ns}{$_});
130             }
131             }
132              
133 337 50       788 print ("Signing XML\n") if $DEBUG;
134              
135 337         1039 my @ids_to_sign = $self->_get_ids_to_sign();
136              
137 337         752 foreach my $signid (@ids_to_sign) {
138              
139 345 50       1095 print ("Signing ID $signid\n") if $DEBUG;
140              
141             # Temporarily create the Signature XML from the part
142             # TODO: ths section needs a rewrite to create the xml in
143             # a better way.
144              
145             # Create a Reference xml fragment including digest section
146 345         1568 my $digest_xml = $self->_reference_xml( $signid, "REPLACE DIGEST " . $signid );
147              
148             # Create a SignedInfo xml fragment including digest section
149 345         957 my $signed_info = $self->_signedinfo_xml( $digest_xml );
150              
151             # Create a Signature xml fragment including SignedInfo section
152 345         1098 my $signature_xml = $self->_signature_xml( $signed_info, 'REPLACE SIGNATURE ' . $signid );
153              
154 345 50       880 print ("Sign ID: $signid\n") if $DEBUG;
155              
156             # Get the XML note to sign base on the ID
157 345         880 my $xml = $self->_get_xml_to_sign($signid);
158              
159             # Set the namespace but do not apply it to the XML
160 345         1302 $xml->setNamespace("http://www.w3.org/2000/09/xmldsig#", "dsig", 0);
161              
162             # Canonicalize the XML to http://www.w3.org/2001/10/xml-exc-c14n#
163             # TODO Change the Canonicalization method in the xml fragment from _signedinfo_xml
164             # <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
165             # <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
166 345         5743 my $xml_canon = $xml->toStringEC14N();
167              
168 345 100       37657 if(my $ref = Digest::SHA->can($self->{ digest_hash })) {
    50          
169 311         780 $self->{digest_method} = $ref;
170             }
171             elsif ( $ref = Crypt::Digest::RIPEMD160->can($self->{ digest_hash })) {
172 34         101 $self->{digest_method} = $ref;
173             }
174             else {
175 0         0 die("Can't handle $self->{ digest_hash }");
176             }
177              
178             # Calculate the digest of the XML being signed
179 345         4362 my $bin_digest = $self->{digest_method}->( Encode::encode_utf8( $xml_canon ));
180 345         1845 my $digest = encode_base64( $bin_digest, '' );
181 345 50       930 print (" Digest: $digest\n") if $DEBUG;
182              
183             # Display the ID of the XML being signed for debugging
184 345         570 my $reference = $signid; #$self->{parser}->findvalue('//@ID', $xml);
185 345 50       681 print (" Reference URI: $reference\n") if $DEBUG;
186              
187             # Add the Signature to the xml being signed
188 345         1263 $xml->appendWellBalancedChunk($signature_xml, 'UTF-8');
189              
190             # Canonicalize the SignedInfo to http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments
191             # TODO Change the Canonicalization method in the xml fragment from _signedinfo_xml
192              
193 345         76103 my ($signature_node) = $xml->findnodes(
194             './dsig:Signature', $xml);
195 345         12709 my ($signed_info_node) = $xml->findnodes(
196             './dsig:Signature/dsig:SignedInfo',$xml);
197              
198             # Add the digest value to the Signed info
199 345         9520 my ($digest_value_node) = $xml->findnodes(
200             './dsig:Signature/dsig:SignedInfo/dsig:Reference/dsig:DigestValue', $signature_node);
201 345         9984 $digest_value_node->removeChildNodes();
202 345         1521 $digest_value_node->appendText($digest);
203              
204             # At this point the SignedInfo includes the information
205             # to allow us to use the _canonicalize_xml with the $signature_node
206 345         906 my $signed_info_canon = $self->_canonicalize_xml($signed_info_node, $signature_node);
207              
208             # Calculate the signature of the Canonical Form of SignedInfo
209 345         6626 my $signature;
210 345 100       1543 if ($self->{key_type} eq 'dsa') {
    100          
    100          
211 58         194 $signature = encode_base64( $self->_calc_dsa_signature( $signed_info_canon ), "\n" );
212             } elsif ($self->{key_type} eq 'ecdsa') {
213 172         560 $signature = encode_base64( $self->_calc_ecdsa_signature( $signed_info_canon ), "\n" );
214             } elsif ($self->{key_type} eq 'rsa') {
215 109         314 $signature = encode_base64( $self->_calc_rsa_signature( $signed_info_canon ), "\n" );
216             } else {
217 6 50       22 if ( defined $self->{ hmac_key } ) {
218 6         24 $signature = encode_base64( $self->_calc_hmac_signature( $signed_info_canon ), "\n" );
219             } else {
220 0         0 die "No Signature signing method provided";
221             }
222             }
223              
224             # Add the Signature to the SignatureValue
225 345         1698 my ($signature_value_node) = $xml->findnodes(
226             './dsig:Signature/dsig:SignatureValue', $signature_node);
227 345         15465 $signature_value_node->removeChildNodes();
228 345         1704 $signature_value_node->appendText($signature);
229              
230 345 50       1570 print ("\n\n\n SignatureValue:\n" . $signature_value_node . "\n\n\n") if $DEBUG;
231             }
232              
233 337         15755 return $dom->toString;
234             }
235              
236              
237             sub verify {
238 356     356 1 14388 my $self = shift;
239 356         716 delete $self->{signer_cert};
240 356         782 my ($xml) = @_;
241              
242 356         1328 my $dom = XML::LibXML->load_xml( string => $xml );
243              
244 356         105020 $self->{ parser } = XML::LibXML::XPathContext->new($dom);
245 356         2375 $self->{ parser }->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
246 356         1421 $self->{ parser }->registerNs('ec', 'http://www.w3.org/2001/10/xml-exc-c14n#');
247 356         1165 $self->{ parser }->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
248 356         1113 $self->{ parser }->registerNs('ecdsa', 'http://www.w3.org/2001/04/xmldsig-more#');
249              
250 356         1173 my $signature_nodeset = $self->{ parser }->findnodes('//dsig:Signature');
251              
252 356         16892 my $numsigs = $signature_nodeset->size();
253 356 50       2309 print ("NodeSet Size: $numsigs\n") if $DEBUG;
254              
255             # Loop through each Signature in the document checking each
256 356         543 my $i;
257 356         974 while (my $signature_node = $signature_nodeset->shift()) {
258 369         4702 $i++;
259 369 50       732 print ("\nSignature $i\n") if $DEBUG;
260              
261             # Get SignedInfo Reference ID
262             my $reference = $self->{ parser }->findvalue(
263 369         1112 'dsig:SignedInfo/dsig:Reference/@URI', $signature_node);
264 369         31553 $reference =~ s/#//g;
265              
266 369 50       1106 print (" Reference URI: $reference\n") if $DEBUG;
267              
268             # The reference ID must point to something in the document
269             # if not disregard it and look for another signature
270             # TODO check to ensure that if there is only a single reference
271             # like this it won't accidentally validate
272 369 100       1551 if (! $self->{ parser }->findvalue('//*[@ID=\''. $reference . '\']')) {
273 2 50       253 print (" Signature reference $reference is not signing anything in this xml\n") if $DEBUG;
274 2 100       8 if ($numsigs <= 1) {
275 1         6 return 0;
276             }
277             else {
278 1         5 next;
279             }
280             }
281              
282             # Get SignedInfo DigestMethod Algorithim
283             my $digest_method = $self->{ parser }->findvalue(
284 367         36253 'dsig:SignedInfo/dsig:Reference/dsig:DigestMethod/@Algorithm', $signature_node);
285 367         33215 $digest_method =~ s/^.*[#]//;
286 367 50       1202 print (" Digest Method: $digest_method\n") if $DEBUG;
287              
288             # Get the DigestValue used to verify Canonical XML
289             # Note that the digest may have embedded newlines in the XML
290             # Decode the base64 and encode it with no newlines
291             my $refdigest = encode_base64(decode_base64(_trim($self->{ parser }->findvalue(
292 367         1100 'dsig:SignedInfo/dsig:Reference/dsig:DigestValue', $signature_node))), "");
293 367 50       1305 print (" Digest Value: $refdigest\n") if $DEBUG;
294              
295             # Get the SignatureValue used to verify the SignedInfo
296 367         5740 my $signature = _trim($self->{ parser }->findvalue('dsig:SignatureValue', $signature_node));
297 367 50       1151 print (" Signature: $signature\n") if $DEBUG;
298              
299             # Get SignatureMethod Algorithim
300             my $signature_method = $self->{ parser }->findvalue(
301 367         5218 'dsig:SignedInfo/dsig:SignatureMethod/@Algorithm', $signature_node);
302 367         26140 $signature_method =~ s/^.*[#]//;
303 367         1138 $signature_method =~ s/^rsa-//;
304 367         802 $signature_method =~ s/^dsa-//;
305 367         953 $signature_method =~ s/^ecdsa-//;
306 367         764 $signature_method =~ s/^hmac-//;
307              
308 367         752 $self->{ sig_hash } = $signature_method;
309 367 50       812 print (" SignatureMethod: $signature_method\n") if $DEBUG;
310              
311             # Get the SignedInfo and obtain its Canonical form
312 367         995 my ($signed_info) = $self->{ parser }->findnodes('dsig:SignedInfo', $signature_node);
313 367         13860 my $signed_info_canon = $self->_canonicalize_xml($signed_info, $signature_node);
314              
315 367 50       5911 print "$signed_info_canon\n" if $DEBUG;
316              
317 367 100       2930 if(my $ref = Digest::SHA->can($signature_method)) {
    50          
318 341         894 $self->{sig_method} = $ref;
319             }
320             elsif ( $ref = Crypt::Digest::RIPEMD160->can( $signature_method )) {
321 26         69 $self->{sig_method} = $ref;
322             }
323             else {
324 0         0 die("Can't handle $signature_method");
325             }
326              
327 367 100       1744 if(my $ref = Digest::SHA->can($digest_method)) {
    50          
328 333         690 $self->{digest_method} = $ref;
329             }
330             elsif ( $ref = Crypt::Digest::RIPEMD160->can( $digest_method )) {
331 34         108 $self->{digest_method} = $ref;
332             }
333             else {
334 0         0 die("Can't handle $digest_method");
335             }
336              
337             # If a cert was provided to XML::Sig->new() use it to
338             # verify the SignedInfo signature
339 367 100 66     2021 if (defined $self->{cert_obj}) {
    100          
340             # use the provided cert to verify
341 16 50       71 unless ($self->_verify_x509_cert($self->{cert_obj},$signed_info_canon,$signature)) {
342 0         0 print STDERR "not verified by x509\n";
343 0         0 return 0;
344             }
345             }
346             elsif (!defined $self->{cert_obj} && defined $self->{ hmac_key }) {
347             # use the provided cert to verify
348 12 100       48 unless ($self->_verify_hmac($signed_info_canon,$signature)) {
349 6 50       12 print "not verified by hmac-" . $self->{ sig_hash }, "\n" if $DEBUG;
350 6         21 return 0;
351             }
352             }
353             # Extract the XML provided certificate and use it to
354             # verify the SignedInfo signature
355             else {
356             # extract the certficate or key from the document
357 339         1519 my %verify_dispatch = (
358             'X509Data' => '_verify_x509',
359             'RSAKeyValue' => '_verify_rsa',
360             'DSAKeyValue' => '_verify_dsa',
361             'ECDSAKeyValue' => '_verify_ecdsa',
362             );
363 339         536 my $keyinfo_nodeset;
364 339         665 foreach my $key_info_sig_type ( qw/X509Data RSAKeyValue DSAKeyValue ECDSAKeyValue/ ) {
365 872 100       3837 if ( $key_info_sig_type eq 'X509Data' ) {
366             $keyinfo_nodeset = $self->{ parser }->find(
367 339         1373 "dsig:KeyInfo/dsig:$key_info_sig_type", $signature_node);
368             #print (" keyinfo_nodeset X509Data: $keyinfo_nodeset\n") if $DEBUG;
369             } else {
370             $keyinfo_nodeset = $self->{ parser }->find(
371 533         1669 "dsig:KeyInfo/dsig:KeyValue/dsig:$key_info_sig_type", $signature_node);
372             #print (" keyinfo_nodeset [DR]SAKeyValue: $keyinfo_nodeset\n") if $DEBUG;
373             }
374 872 100       37274 if ( $keyinfo_nodeset->size ) {
375 339         1959 my $verify_method = $verify_dispatch{$key_info_sig_type};
376 339 50       766 print (" Verify Method: $verify_method\n") if $DEBUG;
377 339 50       868 if ( ! $self->$verify_method($keyinfo_nodeset->get_node(0),
378             $signed_info_canon, $signature) ) {
379 0 0       0 print ("keyinfo_nodeset->get_node: " . $keyinfo_nodeset->get_node(0) . "\n") if $DEBUG;
380 0         0 print STDERR "Failed to verify using $verify_method\n";
381 0         0 return 0;
382             } else {
383 339 50       1237 print ("Success Verifying\n") if $DEBUG;
384             }
385 339         766 last;
386             }
387             }
388 339 50 33     1560 die "Unrecognized key type or no KeyInfo in document" unless (
389             $keyinfo_nodeset && $keyinfo_nodeset->size > 0);
390             }
391              
392             # Signature of SignedInfo was verified above now obtain the
393             # Canonical form of the XML and verify the DigestValue of the XML
394              
395             # Remove the Signature from the signed XML
396 361         20435 my $signed_xml = $self->_get_signed_xml( $signature_node );
397 361         4172 $signed_xml->removeChild( $signature_node );
398              
399             # Obtain the Canonical form of the XML
400 361         1065 my $canonical = $self->_transform($signed_xml, $signature_node);
401              
402             # Add the $signature_node back to the $signed_xml to allow other
403             # signatures to be validated if they exist
404 361         10478 $signed_xml->addChild( $signature_node );
405              
406 361 50       903 print ( " Canonical XML: " . $canonical ."\n") if $DEBUG;
407              
408             # Obtain the DigestValue of the Canonical XML
409 361         7435 my $digest = $self->{digest_method}->(Encode::encode_utf8($canonical));
410              
411 361 50       1267 print ( " Reference Digest: " . _trim($refdigest) ."\n") if $DEBUG;
412              
413 361 50       699 print ( " Calculated Digest: ". _trim(encode_base64($digest, '')) ."\n") if $DEBUG;
414              
415             # Return 0 - fail verification on the first XML signature that fails
416 361 50       1384 return 0 unless ($refdigest eq _trim(encode_base64($digest, '')));
417              
418 361 50       1316 print ( "Signature $i Valid\n") if $DEBUG;
419             }
420              
421 349         11915 return 1;
422             }
423              
424              
425             sub signer_cert {
426 12     12 1 5848 my $self = shift;
427 12         52 return $self->{signer_cert};
428             }
429              
430             ##
431             ## _get_ids_to_sign()
432             ##
433             ## Arguments:
434             ##
435             ## Returns: array Value of ID attributes from XML
436             ##
437             ## Finds all the values of the ID attributes in the XML
438             ## and return them in reverse order found. Reverse order
439             ## assumes that the Signatures should be performed on lower
440             ## Nodes first.
441             ##
442             sub _get_ids_to_sign {
443 337     337   533 my $self = shift;
444              
445 337 100       752 if ($self->{id_attr}) {
446 1         5 my $nodes = $self->{parser}->findnodes($self->{id_attr});
447 1 50       52 if ($nodes->size == 0) {
448 0         0 die "Unable to find an attribute node with $self->{id_attr}";
449             }
450 1         9 my $node = $nodes->get_node(1);
451 1         8 return $node->getAttribute('ID');
452              
453             }
454              
455 336         1224 my @id = $self->{parser}->findnodes('//@ID');
456 336         14734 my @ids;
457 336         769 foreach (@id) {
458 344         662 my $i = $_;
459 344         1943 $_ =~ m/^.*\"(.*)\".*$/;
460 344         6325 $i = $1;
461             #//*[@ID='identifier_1']
462 344 50       876 die "You cannot sign an XML document without identifying the element to sign with an ID attribute" unless $i;
463 344         1060 unshift @ids, $i;
464             }
465 336         1753 return @ids;
466              
467              
468             }
469              
470             ##
471             ## _get_xml_to_sign()
472             ##
473             ## Arguments:
474             ## $id: string ID of the Node for the XML to retrieve
475             ##
476             ## Returns: XML NodeSet to sign
477             ##
478             ## Find the XML node with the ID = $id and return the
479             ## XML NodeSet
480             ##
481             sub _get_xml_to_sign {
482 345     345   657 my $self = shift;
483 345         633 my $id = shift;
484 345 50       649 die "You cannot sign an XML document without identifying the element to sign with an ID attribute" unless $id;
485              
486 345         851 my $xpath = "//*[\@ID='$id']";
487 345         927 my ($node) = $self->_get_node( $xpath );
488 345         730 return $node;
489             }
490              
491             ##
492             ## _get_signed_xml($context)
493             ##
494             ## Arguments:
495             ## $context: string XML NodeSet used as context
496             ##
497             ## Returns: XML NodeSet for with ID equal to the URI
498             ##
499             ## Find the XML node with the ID = $URI and return the
500             ## XML NodeSet
501             ##
502             sub _get_signed_xml {
503 361     361   694 my $self = shift;
504 361         649 my ($context) = @_;
505              
506 361         1187 my $id = $self->{parser}->findvalue('./dsig:SignedInfo/dsig:Reference/@URI', $context);
507 361         31143 $id =~ s/^#//;
508 361 50       1161 print (" Signed XML id: $id\n") if $DEBUG;
509              
510 361         1138 $self->{'sign_id'} = $id;
511 361         1009 my $xpath = "//*[\@ID='$id']";
512 361         1005 return $self->_get_node( $xpath, $context );
513             }
514              
515             ##
516             ## _transform($xml, $context)
517             ##
518             ## Arguments:
519             ## $xml: string XML NodeSet
520             ## $context: string XML Context
521             ##
522             ## Returns: string Transformed XML
523             ##
524             ## Canonicalizes/Transforms xml based on the Transforms
525             ## from the SignedInfo.
526             ##
527             sub _transform {
528 361     361   5349 my $self = shift;
529 361         702 my ($xml, $context) = @_;
530              
531 361         999 $context->setNamespace( 'http://www.w3.org/2000/09/xmldsig#', 'dsig' );
532             my $transforms = $self->{parser}->find(
533 361         6743 'dsig:SignedInfo/dsig:Reference/dsig:Transforms/dsig:Transform',
534             $context
535             );
536              
537 361 50       17479 print "_transform\n" if $DEBUG;
538 361         882 foreach my $node ($transforms->get_nodelist) {
539 722         2839 my $alg = $node->getAttribute('Algorithm');
540              
541 722 50       7622 print " Algorithm: $alg\n" if $DEBUG;
542 722 100       2637 if ($alg eq TRANSFORM_ENV_SIG) {
    50          
    50          
    50          
    0          
543             # TODO the xml being passed here currently has the
544             # Signature removed. May be better to do it all here
545 361         670 next;
546             }
547             elsif ($alg eq TRANSFORM_C14N) {
548 0 0       0 print " toStringC14N" if $DEBUG;
549 0         0 $xml = $xml->toStringC14N();
550             }
551             elsif ($alg eq TRANSFORM_C14N_COMMENTS) {
552 0 0       0 print " toStringC14N(1)" if $DEBUG;
553 0         0 $xml = $xml->toStringC14N(1);
554             }
555             elsif ($alg eq TRANSFORM_EXC_C14N) {
556 361         887 my @prefixlist = $self->_find_prefixlist($node);
557 361 50       762 print " toStringEC14N(0, '', @prefixlist)\n" if $DEBUG;
558 361         1136 $xml = $xml->toStringEC14N(0, '', \@prefixlist);
559             }
560             elsif ($alg eq TRANSFORM_EXC_C14N_COMMENTS) {
561 0         0 my @prefixlist = $self->_find_prefixlist($node);
562 0 0       0 print " toStringEC14N(1, '', @prefixlist)\n" if $DEBUG;
563 0         0 $xml = $xml->toStringEC14N(1, '', \@prefixlist);
564             }
565             else {
566 0         0 die "Unsupported transform: $alg";
567             }
568             }
569 361         39021 return $xml;
570             }
571              
572             ##
573             ## _find_prefixlist($node)
574             ##
575             ## Arguments:
576             ## $node: string XML NodeSet
577             ##
578             ## Returns: ARRAY of prefix lists
579             ##
580             ## Generate an array of prefix lists defined in InclusiveNamespaces
581             ##
582             sub _find_prefixlist {
583 361     361   530 my $self = shift;
584 361         682 my ($node) = @_;
585 361         915 my @children = $node->getChildrenByLocalName('InclusiveNamespaces');
586              
587 361         3448 my $prefixlist = '';
588 361         762 foreach my $child (@children) {
589 4 50       11 if ($child) {
590 4         38 $prefixlist .= $child->getAttribute('PrefixList');
591             }
592 4         60 $prefixlist .= ' ';
593             }
594 361         1001 return split / /, $prefixlist;
595             }
596              
597             ##
598             ## _verify_rsa($context,$canonical,$sig)
599             ##
600             ## Arguments:
601             ## $context: string XML Context to use
602             ## $canonical: string Canonical XML to verify
603             ## $sig: string Base64 encode of RSA Signature
604             ##
605             ## Returns: integer (1 True, 0 False) if signature is valid
606             ##
607             ## Verify the RSA signature of Canonical XML
608             ##
609             sub _verify_rsa {
610 53     53   457 my $self = shift;
611 53         130 my ($context,$canonical,$sig) = @_;
612              
613             # Generate Public Key from XML
614 53         156 my $mod = _trim($self->{parser}->findvalue('dsig:Modulus', $context));
615 53         186 my $modBin = decode_base64( $mod );
616 53         913 my $exp = _trim($self->{parser}->findvalue('dsig:Exponent', $context));
617 53         206 my $expBin = decode_base64( $exp );
618 53         910 my $n = Crypt::OpenSSL::Bignum->new_from_bin($modBin);
619 53         154 my $e = Crypt::OpenSSL::Bignum->new_from_bin($expBin);
620 53         1630 my $rsa_pub = Crypt::OpenSSL::RSA->new_key_from_parameters( $n, $e );
621              
622             # Decode signature and verify
623 53         3966 my $sig_hash = 'use_' . $self->{ sig_hash } . '_hash';
624 53         267 $rsa_pub->$sig_hash;
625 53         227 my $bin_signature = decode_base64($sig);
626 53 50       5909 return 1 if ($rsa_pub->verify( $canonical, $bin_signature ));
627 0         0 return 0;
628             }
629              
630             ##
631             ## _clean_x509($cert)
632             ##
633             ## Arguments:
634             ## $cert: string Certificate in base64 from XML
635             ##
636             ## Returns: string Certificate in Valid PEM format
637             ##
638             ## Reformats Certifcate string into PEM format 64 characters
639             ## with proper header and footer
640             ##
641             sub _clean_x509 {
642 107     107   1729 my $self = shift;
643 107         249 my ($cert) = @_;
644              
645 107 50       291 $cert = $cert->value() if(ref $cert);
646 107         273 chomp($cert);
647              
648             # rewrap the base64 data from the certificate; it may not be
649             # wrapped at 64 characters as PEM requires
650 107         1975 $cert =~ s/\n//g;
651              
652 107         297 my @lines;
653 107         1195 while (length $cert > 64) {
654 2183         15128 push @lines, substr $cert, 0, 64, '';
655             }
656 107         281 push @lines, $cert;
657              
658 107         749 $cert = join "\n", @lines;
659              
660 107         463 $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "\n-----END CERTIFICATE-----\n";
661 107         464 return $cert;
662             }
663              
664             ##
665             ## _verify_x509($context,$canonical,$sig)
666             ##
667             ## Arguments:
668             ## $context: string XML Context to use
669             ## $canonical: string Canonical XML to verify
670             ## $sig: string Base64 encode of RSA Signature
671             ##
672             ## Returns: integer (1 True, 0 False) if signature is valid
673             ##
674             ## Verify the RSA signature of Canonical XML using an X509
675             ##
676             sub _verify_x509 {
677 107     107   998 my $self = shift;
678 107         347 my ($context,$canonical,$sig) = @_;
679              
680 107         176 eval {
681 107         2699 require Crypt::OpenSSL::X509;
682             };
683 107 50       122137 confess "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certificates" if $@;
684              
685             # Generate Public Key from XML
686 107         353 my $certificate = _trim($self->{parser}->findvalue('dsig:X509Certificate', $context));
687              
688             # This is added because the X509 parser requires it for self-identification
689 107         420 $certificate = $self->_clean_x509($certificate);
690              
691 107         8870 my $cert = Crypt::OpenSSL::X509->new_from_string($certificate);
692              
693 107         496 return $self->_verify_x509_cert($cert, $canonical, $sig);
694             }
695              
696             ##
697             ## _verify_x509_cert($cert,$canonical,$sig)
698             ##
699             ## Arguments:
700             ## $cert: string X509 Certificate
701             ## $canonical: string Canonical XML to verify
702             ## $sig: string Base64 encode of [EC|R]SA Signature
703             ##
704             ## Returns: integer (1 True, 0 False) if signature is valid
705             ##
706             ## Verify the X509 signature of Canonical XML
707             ##
708             sub _verify_x509_cert {
709 123     123   215 my $self = shift;
710 123         345 my ($cert, $canonical, $sig) = @_;
711              
712             # Decode signature and verify
713 123         549 my $bin_signature = decode_base64($sig);
714              
715 123 100       1586 if ($cert->key_alg_name eq 'id-ecPublicKey') {
    100          
716 50 50       118 eval {require Crypt::PK::ECC; CryptX->VERSION('0.036'); 1}
  50         292  
  50         697  
  50         304  
717             or confess "Crypt::PK::ECC 0.036+ needs to be installed so
718             that we can handle ECDSA signatures";
719 50         5225 my $ecdsa_pub = Crypt::PK::ECC->new(\$cert->pubkey);
720              
721 50         279289 my $ecdsa_hash = $self->{rsa_hash};
722              
723             # Signature is stored as the concatenation of r and s.
724             # verify_message_rfc7518 expects that format
725 50 50       172691 if ($ecdsa_pub->verify_message_rfc7518( $bin_signature, $canonical, uc($self->{sig_hash}) )) {
726 50         363 $self->{signer_cert} = $cert;
727 50         528 return 1;
728             }
729             }
730             elsif ($cert->key_alg_name eq 'dsaEncryption') {
731 2         5 eval {
732 2         14 require Crypt::OpenSSL::DSA;
733             };
734 2 50       12 confess "Crypt::OpenSSL::DSA needs to be installed so
735             that we can handle DSA X509 certificates" if $@;
736              
737 2         161 my $dsa_pub = Crypt::OpenSSL::DSA->read_pub_key_str( $cert->pubkey );
738 2         138 my $sig_size = ($dsa_pub->get_sig_size - 8)/2;
739             #my ($r, $s) = unpack('a20a20', $bin_signature);
740 2         28 my $unpk = "a" . $sig_size . "a" . $sig_size;
741 2         15 my ($r, $s) = unpack($unpk, $bin_signature);
742              
743             # Create a new Signature Object from r and s
744 2         10 my $sigobj = Crypt::OpenSSL::DSA::Signature->new();
745 2         13 $sigobj->set_r($r);
746 2         8 $sigobj->set_s($s);
747              
748 2 50       2708 if ($dsa_pub->do_verify($self->{sig_method}->($canonical), $sigobj)) {
749 2         14 $self->{signer_cert} = $cert;
750 2         25 return 1;
751             }
752             }
753             else {
754 71         151 eval {
755 71         2973 require Crypt::OpenSSL::RSA;
756             };
757 71 50       23090 confess "Crypt::OpenSSL::RSA needs to be installed so
758             that we can handle X509 certificates" if $@;
759              
760 71         3808 my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($cert->pubkey);
761              
762 71         8766 my $sig_hash = 'use_' . $self->{sig_hash} . '_hash';
763 71         394 $rsa_pub->$sig_hash();
764             # If successful verify, store the signer's cert for validation
765 71 50       7543 if ($rsa_pub->verify( $canonical, $bin_signature )) {
766 71         325 $self->{signer_cert} = $cert;
767 71         624 return 1;
768             }
769             }
770              
771 0         0 return 0;
772             }
773              
774             ##
775             ## _zero_fill_buffer($bits)
776             ##
777             ## Arguments:
778             ## $bits: number of bits to set to zero
779             ##
780             ## Returns: Zero filled bit buffer of size $bits
781             ##
782             ## Create a buffer with all bits set to 0
783             ##
784             sub _zero_fill_buffer {
785 58     58   100 my $bits = shift;
786             # set all bit to zero
787 58         105 my $v = '';
788 58         185 for (my $i = 0; $i < $bits; $i++) {
789 23936         50130 vec($v, $i, 1) = 0;
790             }
791 58         137 return $v;
792             }
793              
794             ##
795             ## _concat_dsa_sig_r_s(\$buffer,$r,$s)
796             ##
797             ## Arguments:
798             ## $buffer: Zero Filled bit buffer
799             ## $r: octet stream
800             ## $s: octet stream
801             ##
802             ## Combine r and s components of DSA signature
803             ##
804             sub _concat_dsa_sig_r_s {
805              
806 58     58   175 my ($buffer, $r, $s, $sig_size) = @_;
807 58         110 my $bits_r = (length($r)*8)-1;
808 58         92 my $bits_s = (length($s)*8)-1;
809              
810 58         136 my $halfsize = $sig_size / 2;
811              
812             # Place $s right justified in $v starting at bit 319
813 58         169 for (my $i = $bits_s; $i >=0; $i--) {
814 11960         28294 vec($$buffer, $halfsize + $i + (($halfsize -1) - $bits_s) , 1) = vec($s, $i, 1);
815             }
816              
817             # Place $r right justified in $v starting at bit 159
818 58         179 for (my $i = $bits_r; $i >= 0 ; $i--) {
819 11960         27566 vec($$buffer, $i + (($halfsize -1) - $bits_r) , 1) = vec($r, $i, 1);
820             }
821              
822             }
823              
824             ##
825             ## _verify_dsa($context,$canonical,$sig)
826             ##
827             ## Arguments:
828             ## $context: string XML Context to use
829             ## $canonical: string Canonical XML to verify
830             ## $sig: string Base64 encode 40 byte string of r and s
831             ##
832             ## Returns: integer (1 True, 0 False) if signature is valid
833             ##
834             ## Verify the DSA signature of Canonical XML
835             ##
836             sub _verify_dsa {
837 57     57   465 my $self = shift;
838 57         137 my ($context,$canonical,$sig) = @_;
839              
840 57         92 eval {
841 57         338 require Crypt::OpenSSL::DSA;
842             };
843 57 50       144 confess "Crypt::OpenSSL::DSA needs to be installed so
844             that we can handle DSA signatures" if $@;
845              
846             # Generate Public Key from XML
847 57         164 my $p = decode_base64(_trim($self->{parser}->findvalue('dsig:P', $context)));
848 57         201 my $q = decode_base64(_trim($self->{parser}->findvalue('dsig:Q', $context)));
849 57         172 my $g = decode_base64(_trim($self->{parser}->findvalue('dsig:G', $context)));
850 57         248 my $y = decode_base64(_trim($self->{parser}->findvalue('dsig:Y', $context)));
851 57         217 my $dsa_pub = Crypt::OpenSSL::DSA->new();
852 57         1100 $dsa_pub->set_p($p);
853 57         277 $dsa_pub->set_q($q);
854 57         206 $dsa_pub->set_g($g);
855 57         158 $dsa_pub->set_pub_key($y);
856              
857             # Decode signature and verify
858 57         147 my $bin_signature = decode_base64($sig);
859              
860             # https://www.w3.org/TR/2002/REC-xmldsig-core-20020212/#sec-SignatureAlg
861             # The output of the DSA algorithm consists of a pair of integers
862             # The signature value consists of the base64 encoding of the
863             # concatenation of r and s in that order ($r . $s)
864             # Binary Signature is stored as a concatenation of r and s
865 57         274 my $sig_size = ($dsa_pub->get_sig_size - 8)/2;
866 57         441 my $unpk = "a" . $sig_size . "a" . $sig_size;
867 57         279 my ($r, $s) = unpack($unpk, $bin_signature);
868              
869             # Create a new Signature Object from r and s
870 57         200 my $sigobj = Crypt::OpenSSL::DSA::Signature->new();
871 57         187 $sigobj->set_r($r);
872 57         176 $sigobj->set_s($s);
873              
874             # DSA signatures are limited to a message body of 20 characters, so a sha1 digest is taken
875 57 50       55278 return 1 if ($dsa_pub->do_verify( $self->{sig_method}->($canonical), $sigobj ));
876 0         0 return 0;
877             }
878              
879             ##
880             ## _verify_ecdsa($context,$canonical,$sig)
881             ##
882             ## Arguments:
883             ## $context: string XML Context to use
884             ## $canonical: string Canonical XML to verify
885             ## $sig: string Base64 encoded
886             ##
887             ## Returns: integer (1 True, 0 False) if signature is valid
888             ##
889             ## Verify the ECDSA signature of Canonical XML
890             ##
891             sub _verify_ecdsa {
892 122     122   1198 my $self = shift;
893 122         357 my ($context,$canonical,$sig) = @_;
894              
895 122 50       229 eval {require Crypt::PK::ECC; CryptX->VERSION('0.036'); 1}
  122         841  
  122         1399  
  122         616  
896             or confess "Crypt::PK::ECC 0.036+ needs to be installed so
897             that we can handle ECDSA signatures";
898             # Generate Public Key from XML
899 122         445 my $oid = _trim($self->{parser}->findvalue('.//dsig:NamedCurve/@URN', $context));
900              
901 23     23   13320 use URI ();
  23         109021  
  23         4769  
902 122         1181 my $u1 = URI->new($oid);
903 122         28995 $oid = $u1->nss;
904              
905 122         3530 my %curve_name = (
906             '1.2.840.10045.3.1.1' => 'secp192r1',
907             '1.3.132.0.33' => 'secp224r1',
908             '1.2.840.10045.3.1.7' => 'secp256r1',
909             '1.3.132.0.34' => 'secp384r1',
910             '1.3.132.0.35' => 'secp521r1',
911             '1.3.36.3.3.2.8.1.1.1' => 'brainpoolP160r1',
912             '1.3.36.3.3.2.8.1.1.3' => 'brainpoolP192r1',
913             '1.3.36.3.3.2.8.1.1.5' => 'brainpoolP224r1',
914             '1.3.36.3.3.2.8.1.1.7' => 'brainpoolP256r1',
915             '1.3.36.3.3.2.8.1.1.9' => 'brainpoolP320r1',
916             '1.3.36.3.3.2.8.1.1.11' => 'brainpoolP384r1',
917             '1.3.36.3.3.2.8.1.1.13' => 'brainpoolP512r1',
918             );
919              
920 122         414 my $x = $self->{parser}->findvalue('.//dsig:PublicKey/dsig:X/@Value', $context);
921 122         9106 my $y = $self->{parser}->findvalue('.//dsig:PublicKey/dsig:Y/@Value', $context);
922              
923 122         7631 my $ecdsa_pub = Crypt::PK::ECC->new();
924              
925             $ecdsa_pub->import_key({
926             kty => "EC",
927 122         7526 curve_name => $curve_name{ $oid },
928             pub_x => $x,
929             pub_y => $y,
930             });
931              
932 122         669390 my $bin_signature = decode_base64($sig);
933              
934             # verify_message_rfc7518 is used to verify signature stored as a
935             # concatenation of integers r and s
936             return 1 if ($ecdsa_pub->verify_message_rfc7518(
937             $bin_signature,
938             $canonical,
939 122 50       423689 uc($self->{sig_hash}))
940             );
941 0         0 return 0;
942             }
943              
944             ##
945             ## _verify_hmac($canonical, $sig)
946             ##
947             ## Arguments:
948             ## $canonical: string Canonical XML to verify
949             ## $sig: string Base64 encode of HMAC Signature
950             ##
951             ## Returns: integer (1 True, 0 False) if signature is valid
952             ##
953             ## Verify the HMAC signature of Canonical XML
954             ##
955             sub _verify_hmac {
956 12     12   23 my $self = shift;
957 12         28 my ($canonical, $sig) = @_;
958              
959             # Decode signature and verify
960 12         36 my $bin_signature = decode_base64($sig);
961 23     23   10885 use Crypt::Mac::HMAC qw( hmac );
  23         28009  
  23         70888  
962 12 50       29 if ( defined $self->{ hmac_key } ) {
963 12 50       24 print (" Verifying SignedInfo using hmac-", $self->{ sig_hash }, "\n") if $DEBUG;
964 12 100       119 if ( my $ref = Digest::SHA->can('hmac_' . $self->{ sig_hash }) ) {
    50          
965 10 100       34 if ($bin_signature eq $self->_calc_hmac_signature( $canonical )) {
966 5         20 return 1;
967             }
968             else {
969 5         19 return 0;
970             }
971             }
972             elsif ( $ref = Crypt::Digest::RIPEMD160->can($self->{ sig_hash })) {
973 2 100       7 if ($bin_signature eq $self->_calc_hmac_signature( $canonical )) {
974 1         5 return 1;
975             }
976             else {
977 1         6 return 0;
978             }
979             }
980             else {
981 0         0 die("Can't handle $self->{ sig_hash }");
982             }
983              
984             } else {
985 0         0 return 0;
986             }
987             }
988              
989             ##
990             ## _get_node($xpath, context)
991             ##
992             ## Arguments:
993             ## $xpath: string XML XPath to use
994             ## $context: string XML context
995             ##
996             ## Returns: string XML NodeSet
997             ##
998             ## Return a NodeSet based on the xpath string
999             ##
1000             sub _get_node {
1001 706     706   1063 my $self = shift;
1002 706         1320 my ($xpath, $context) = @_;
1003 706         1020 my $nodeset;
1004 706 100       1737 if ($context) {
1005 361         2750 $nodeset = $self->{parser}->find($xpath, $context);
1006             } else {
1007 345         1110 $nodeset = $self->{parser}->find($xpath);
1008             }
1009 706         44268 foreach my $node ($nodeset->get_nodelist) {
1010 706         5468 return $node;
1011             }
1012             }
1013              
1014             # TODO remove unused?
1015             sub _get_node_as_text {
1016 0     0   0 my $self = shift;
1017 0         0 my ($xpath, $context) = @_;
1018 0         0 my $node = $self->_get_node($xpath, $context);
1019 0 0       0 if ($node) {
1020 0         0 return $node->toString;
1021             } else {
1022 0         0 return '';
1023             }
1024             }
1025              
1026             # TODO remove unused?
1027             sub _transform_env_sig {
1028 0     0   0 my $self = shift;
1029 0         0 my ($str) = @_;
1030 0         0 my $prefix = '';
1031 0 0 0     0 if (defined $self->{dsig_prefix} && length $self->{dsig_prefix}) {
1032 0         0 $prefix = $self->{dsig_prefix} . ':';
1033             }
1034              
1035             # This removes the first Signature tag from the XML - even if there is another XML tree with another Signature inside and that comes first.
1036             # TODO: Remove the outermost Signature only.
1037              
1038 0         0 $str =~ s/(<${prefix}Signature(.*?)>(.*?)\<\/${prefix}Signature>)//is;
1039              
1040 0         0 return $str;
1041             }
1042              
1043             ##
1044             ## _trim($string)
1045             ##
1046             ## Arguments:
1047             ## $string: string String to remove whitespace
1048             ##
1049             ## Returns: string Trimmed String
1050             ##
1051             ## Trim the whitespace from the begining and end of the string
1052             ##
1053             sub _trim {
1054 1767     1767   88201 my $string = shift;
1055 1767         7658 $string =~ s/^\s+//;
1056 1767         11230 $string =~ s/\s+$//;
1057 1767         6706 return $string;
1058             }
1059              
1060             ##
1061             ## _load_ecdsa_key($key_text)
1062             ##
1063             ## Arguments:
1064             ## $key_text: string ECDSA Private Key as String
1065             ##
1066             ## Returns: nothing
1067             ##
1068             ## Populate:
1069             ## self->{KeyInfo}
1070             ## self->{key_obj}
1071             ## self->{key_type}
1072             ##
1073             sub _load_ecdsa_key {
1074 170     170   347 my $self = shift;
1075 170         306 my $key_text = shift;
1076              
1077 170 50       302 eval {require Crypt::PK::ECC; CryptX->VERSION('0.036'); 1}
  170         4531  
  170         64428  
  170         955  
1078             or confess "Crypt::PK::ECC 0.036+ needs to be installed so
1079             that we can handle ECDSA signatures";
1080              
1081 170         1145 my $ecdsa_key = Crypt::PK::ECC->new(\$key_text);
1082              
1083 170 50       1868507 if ( $ecdsa_key ) {
1084 170         453 $self->{ key_obj } = $ecdsa_key;
1085              
1086 170         10329 my $key_hash = $ecdsa_key->key2hash;
1087              
1088 170         462 my $oid = $key_hash->{ curve_oid };
1089 170         332 my $x = $key_hash->{ pub_x };
1090 170         251 my $y = $key_hash->{ pub_y };
1091              
1092 170         727 $self->{KeyInfo} = "<dsig:KeyInfo>
1093             <dsig:KeyValue>
1094             <dsig:ECDSAKeyValue>
1095             <dsig:DomainParameters>
1096             <dsig:NamedCurve URN=\"urn:oid:$oid\" />
1097             </dsig:DomainParameters>
1098             <dsig:PublicKey>
1099             <dsig:X Value=\"$x\" />
1100             <dsig:Y Value=\"$y\" />
1101             </dsig:PublicKey>
1102             </dsig:ECDSAKeyValue>
1103             </dsig:KeyValue>
1104             </dsig:KeyInfo>";
1105 170         1214 $self->{key_type} = 'ecdsa';
1106             }
1107             else {
1108 0         0 confess "did not get a new Crypt::PK::ECC object";
1109             }
1110             }
1111              
1112             ##
1113             ## _load_dsa_key($key_text)
1114             ##
1115             ## Arguments:
1116             ## $key_text: string DSA Private Key as String
1117             ##
1118             ## Returns: nothing
1119             ##
1120             ## Populate:
1121             ## self->{KeyInfo}
1122             ## self->{key_obj}
1123             ## self->{key_type}
1124             ##
1125             sub _load_dsa_key {
1126 56     56   109 my $self = shift;
1127 56         93 my $key_text = shift;
1128              
1129 56         111 eval {
1130 56         6614 require Crypt::OpenSSL::DSA;
1131             };
1132              
1133 56 50       24340 confess "Crypt::OpenSSL::DSA needs to be installed so that we can handle DSA keys." if $@;
1134              
1135 56         363 my $dsa_key = Crypt::OpenSSL::DSA->read_priv_key_str( $key_text );
1136              
1137 56 50       2741 if ( $dsa_key ) {
1138 56         141 $self->{ key_obj } = $dsa_key;
1139 56         462 my $g = encode_base64( $dsa_key->get_g(), '' );
1140 56         313 my $p = encode_base64( $dsa_key->get_p(), '' );
1141 56         261 my $q = encode_base64( $dsa_key->get_q(), '' );
1142 56         700 my $y = encode_base64( $dsa_key->get_pub_key(), '' );
1143              
1144 56         332 $self->{KeyInfo} = "<dsig:KeyInfo>
1145             <dsig:KeyValue>
1146             <dsig:DSAKeyValue>
1147             <dsig:P>$p</dsig:P>
1148             <dsig:Q>$q</dsig:Q>
1149             <dsig:G>$g</dsig:G>
1150             <dsig:Y>$y</dsig:Y>
1151             </dsig:DSAKeyValue>
1152             </dsig:KeyValue>
1153             </dsig:KeyInfo>";
1154 56         169 $self->{key_type} = 'dsa';
1155             }
1156             else {
1157 0         0 confess "did not get a new Crypt::OpenSSL::RSA object";
1158             }
1159             }
1160              
1161             ##
1162             ## _load_rsa_key($key_text)
1163             ##
1164             ## Arguments:
1165             ## $key_text: string RSA Private Key as String
1166             ##
1167             ## Returns: nothing
1168             ##
1169             ## Populate:
1170             ## self->{KeyInfo}
1171             ## self->{key_obj}
1172             ## self->{key_type}
1173             ##
1174             sub _load_rsa_key {
1175 109     109   204 my $self = shift;
1176 109         307 my ($key_text) = @_;
1177              
1178 109         215 eval {
1179 109         7010 require Crypt::OpenSSL::RSA;
1180             };
1181 109 50       77927 confess "Crypt::OpenSSL::RSA needs to be installed so that we can handle RSA keys." if $@;
1182              
1183 109         5307 my $rsaKey = Crypt::OpenSSL::RSA->new_private_key( $key_text );
1184              
1185 109 50       514 if ( $rsaKey ) {
1186 109         421 $rsaKey->use_pkcs1_padding();
1187 109         240 $self->{ key_obj } = $rsaKey;
1188 109         222 $self->{ key_type } = 'rsa';
1189              
1190 109 100       327 if (!$self->{ x509 }) {
1191 55         1608 my $bigNum = ( $rsaKey->get_key_parameters() )[1];
1192 55         7092 my $bin = $bigNum->to_bin();
1193 55         223 my $exp = encode_base64( $bin, '' );
1194              
1195 55         1350 $bigNum = ( $rsaKey->get_key_parameters() )[0];
1196 55         1661 $bin = $bigNum->to_bin();
1197 55         196 my $mod = encode_base64( $bin, '' );
1198 55         317 $self->{KeyInfo} = "<dsig:KeyInfo>
1199             <dsig:KeyValue>
1200             <dsig:RSAKeyValue>
1201             <dsig:Modulus>$mod</dsig:Modulus>
1202             <dsig:Exponent>$exp</dsig:Exponent>
1203             </dsig:RSAKeyValue>
1204             </dsig:KeyValue>
1205             </dsig:KeyInfo>";
1206             }
1207             }
1208             else {
1209 0         0 confess "did not get a new Crypt::OpenSSL::RSA object";
1210             }
1211             }
1212              
1213             ##
1214             ## _load_hmac_key_info()
1215             ##
1216             ## Arguments:
1217             ## none
1218             ##
1219             ## Returns: nothing
1220             ##
1221             ## Populate:
1222             ## self->{KeyInfo}
1223             ##
1224             sub _load_hmac_key_info {
1225 18     18   24 my $self = shift;
1226              
1227 18 50       74 if (! defined $self->{ key_name }) {
1228 0         0 return;
1229             }
1230              
1231 18         77 $self->{KeyInfo} = qq{<dsig:KeyInfo><dsig:KeyName>$self->{key_name}</dsig:KeyName></dsig:KeyInfo>};
1232             }
1233              
1234             ##
1235             ## _load_x509_key($key_text)
1236             ##
1237             ## Arguments:
1238             ## $key_text: string RSA Private Key as String
1239             ##
1240             ## Returns: nothing
1241             ##
1242             ## Populate:
1243             ## self->{key_obj}
1244             ## self->{key_type}
1245             ##
1246             sub _load_x509_key {
1247 0     0   0 my $self = shift;
1248 0         0 my $key_text = shift;
1249              
1250 0         0 eval {
1251 0         0 require Crypt::OpenSSL::X509;
1252             };
1253 0 0       0 confess "Crypt::OpenSSL::X509 needs to be installed so that we
1254             can handle X509 Certificates." if $@;
1255              
1256 0         0 my $x509Key = Crypt::OpenSSL::X509->new_private_key( $key_text );
1257              
1258 0 0       0 if ( $x509Key ) {
1259 0         0 $x509Key->use_pkcs1_padding();
1260 0         0 $self->{ key_obj } = $x509Key;
1261 0         0 $self->{key_type} = 'x509';
1262             }
1263             else {
1264 0         0 confess "did not get a new Crypt::OpenSSL::X509 object";
1265             }
1266             }
1267              
1268             ##
1269             ## _load_cert_file()
1270             ##
1271             ## Arguments: none
1272             ##
1273             ## Returns: nothing
1274             ##
1275             ## Read the file name from $self->{ cert } and
1276             ## Populate:
1277             ## self->{key_obj}
1278             ## $self->{KeyInfo}
1279             ##
1280             sub _load_cert_file {
1281 108     108   253 my $self = shift;
1282              
1283 108         170 eval {
1284 108         6865 require Crypt::OpenSSL::X509;
1285             };
1286              
1287 108 50       347150 confess "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certs." if $@;
1288              
1289 108         294 my $file = $self->{ cert };
1290 108 50       4638 if ( open my $CERT, '<', $file ) {
1291 108         472 my $text = '';
1292 108         658 local $/ = undef;
1293 108         2887 $text = <$CERT>;
1294 108         1317 close $CERT;
1295              
1296 108         10606 my $cert = Crypt::OpenSSL::X509->new_from_string($text);
1297 108 50       619 if ( $cert ) {
1298 108         307 $self->{ cert_obj } = $cert;
1299 108         2555 my $cert_text = $cert->as_string;
1300 108         1381 $cert_text =~ s/-----[^-]*-----//gm;
1301 108         430 $self->{KeyInfo} = "<dsig:KeyInfo><dsig:X509Data><dsig:X509Certificate>\n"._trim($cert_text)."\n</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo>";
1302             }
1303             else {
1304 0         0 confess "Could not load certificate from $file";
1305             }
1306             }
1307             else {
1308 0         0 confess "Could not find certificate file $file";
1309             }
1310              
1311 108         568 return;
1312             }
1313              
1314             ##
1315             ## _load_cert_text()
1316             ##
1317             ## Arguments: none
1318             ##
1319             ## Returns: nothing
1320             ##
1321             ## Read the certificate from $self->{ cert_text } and
1322             ## Populate:
1323             ## self->{key_obj}
1324             ## $self->{KeyInfo}
1325             ##
1326             sub _load_cert_text {
1327 1     1   3 my $self = shift;
1328              
1329 1         2 eval {
1330 1         5 require Crypt::OpenSSL::X509;
1331             };
1332              
1333 1 50       4 confess "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certs." if $@;
1334              
1335 1         3 my $text = $self->{ cert_text };
1336 1         75 my $cert = Crypt::OpenSSL::X509->new_from_string($text);
1337 1 50       5 if ( $cert ) {
1338 1         3 $self->{ cert_obj } = $cert;
1339 1         28 my $cert_text = $cert->as_string;
1340 1         13 $cert_text =~ s/-----[^-]*-----//gm;
1341 1         5 $self->{KeyInfo} = "<dsig:KeyInfo><dsig:X509Data><dsig:X509Certificate>\n"._trim($cert_text)."\n</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo>";
1342             }
1343             else {
1344 0         0 confess "Could not load certificate from given text.";
1345             }
1346              
1347 1         3 return;
1348             }
1349              
1350             ##
1351             ## _load_key($file)
1352             ##
1353             ## Arguments: $self->{ key }
1354             ##
1355             ## Returns: nothing
1356             ##
1357             ## Load the key and process it acording to its headers
1358             ##
1359             sub _load_key {
1360 337     337   650 my $self = shift;
1361 337         634 my $file = $self->{ key };
1362              
1363 337 100       16195 if ( open my $KEY, '<', $file ) {
1364 336         1232 my $text = '';
1365 336         1775 local $/ = undef;
1366 336         10147 $text = <$KEY>;
1367 336         4189 close $KEY;
1368              
1369 336 100       2813 if ( $text =~ m/BEGIN ([DR]SA) PRIVATE KEY/ ) {
    100          
    100          
    50          
1370 163         612 my $key_used = $1;
1371              
1372 163 100       446 if ( $key_used eq 'RSA' ) {
1373 107         439 $self->_load_rsa_key( $text );
1374             }
1375             else {
1376 56         231 $self->_load_dsa_key( $text );
1377             }
1378              
1379 163         1018 return 1;
1380             } elsif ( $text =~ m/BEGIN EC PRIVATE KEY/ ) {
1381 170         640 $self->_load_ecdsa_key( $text );
1382             } elsif ( $text =~ m/BEGIN PRIVATE KEY/ ) {
1383 2         10 $self->_load_rsa_key( $text );
1384             } elsif ($text =~ m/BEGIN CERTIFICATE/) {
1385 0         0 $self->_load_x509_key( $text );
1386             }
1387             else {
1388 1         14 confess "Could not detect type of key $file.";
1389             }
1390             }
1391             else {
1392 1         30 confess "Could not load key $file: $!";
1393             }
1394              
1395 172         784 return;
1396             }
1397              
1398             ##
1399             ## _signature_xml($signed_info,$signature_value)
1400             ##
1401             ## Arguments:
1402             ## $signed_info: string XML String Fragment
1403             ## $signature_value String Base64 Signature Value
1404             ##
1405             ## Returns: string XML fragment
1406             ##
1407             ## Create a XML string of the Signature
1408             ##
1409             sub _signature_xml {
1410 345     345   511 my $self = shift;
1411 345         627 my ($signed_info,$signature_value) = @_;
1412              
1413 345         3061 return qq{<dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
1414             $signed_info
1415             <dsig:SignatureValue>$signature_value</dsig:SignatureValue>
1416             $self->{KeyInfo}
1417             </dsig:Signature>};
1418             }
1419              
1420             ##
1421             ## _signedinfo_xml($digest_xml)
1422             ##
1423             ## Arguments:
1424             ## $digest_xml string XML String Fragment
1425             ##
1426             ## Returns: string XML fragment
1427             ##
1428             ## Create a XML string of the SignedInfo
1429             ##
1430             sub _signedinfo_xml {
1431 345     345   516 my $self = shift;
1432 345         588 my ($digest_xml) = @_;
1433              
1434 345         463 my $algorithm;
1435 345 50 33     1012 if (! defined $self->{key_type} && defined $self->{ hmac_key } ) {
1436 0         0 $self->{key_type} = 'hmac';
1437             }
1438              
1439 345 100 66     1782 if ( $self->{ sig_hash } eq 'sha1' && $self->{key_type} ne 'ecdsa' ) {
    100 66        
    100          
1440 30         118 $algorithm = "http://www.w3.org/2000/09/xmldsig#$self->{key_type}-$self->{ sig_hash }";
1441             }
1442             elsif ( $self->{key_type} eq 'ecdsa' ) {
1443 172 100 66     758 if ( $self->{ sig_hash } eq 'ripemd160' || $self->{ sig_hash } eq 'whirlpool' ) {
1444 12         63 $algorithm = "http://www.w3.org/2007/05/xmldsig-more#$self->{key_type}-$self->{ sig_hash }";
1445             }
1446             else {
1447 160         442 $algorithm = "http://www.w3.org/2001/04/xmldsig-more#$self->{key_type}-$self->{ sig_hash }";
1448             }
1449             }
1450             elsif ( $self->{ key_type } eq 'dsa' && $self->{ sig_hash } eq 'sha256') {
1451 28         75 $algorithm = "http://www.w3.org/2009/xmldsig11#$self->{key_type}-$self->{ sig_hash }";
1452             }
1453             else {
1454 115         313 $algorithm = "http://www.w3.org/2001/04/xmldsig-more#$self->{key_type}-$self->{ sig_hash }";
1455             }
1456              
1457             #return qq{<dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
1458 345         1777 return qq{<dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
1459             <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
1460             <dsig:SignatureMethod Algorithm="$algorithm" />
1461             $digest_xml
1462             </dsig:SignedInfo>};
1463             }
1464              
1465             ##
1466             ## _reference_xml($id)
1467             ##
1468             ## Arguments:
1469             ## $id string XML ID related to the URI
1470             ## $digest string Base64 encoded digest
1471             ##
1472             ## Returns: string XML fragment
1473             ##
1474             ## Create a XML string of the Reference
1475             ##
1476             sub _reference_xml {
1477 345     345   713 my $self = shift;
1478 345         561 my $id = shift;
1479 345         660 my ($digest) = @_;
1480              
1481 345         579 my $algorithm;
1482 345 100 100     2211 if ( $self->{ digest_hash } eq 'sha1') {
    100          
1483 34         135 $algorithm = "http://www.w3.org/2000/09/xmldsig#$self->{ digest_hash }";
1484             }
1485             elsif (($self->{ digest_hash } eq 'sha224') || ($self->{ digest_hash } eq 'sha384')) {
1486 68         192 $algorithm = "http://www.w3.org/2001/04/xmldsig-more#$self->{ digest_hash }";
1487             }
1488             else {
1489 243         583 $algorithm = "http://www.w3.org/2001/04/xmlenc#$self->{ digest_hash }";
1490             }
1491              
1492 345         2220 return qq{<dsig:Reference URI="#$id">
1493             <dsig:Transforms>
1494             <dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
1495             <dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
1496             </dsig:Transforms>
1497             <dsig:DigestMethod Algorithm="$algorithm" />
1498             <dsig:DigestValue>$digest</dsig:DigestValue>
1499             </dsig:Reference>};
1500             }
1501              
1502              
1503             ##
1504             ## _canonicalize_xml($xml, $context)
1505             ##
1506             ## Arguments:
1507             ## $xml: string XML NodeSet
1508             ## $context: string XML Context
1509             ##
1510             ## Returns: string Canonical XML
1511             ##
1512             ## Canonicalizes xml based on the CanonicalizationMethod
1513             ## from the SignedInfo.
1514             ##
1515             sub _canonicalize_xml {
1516 712     712   1124 my $self = shift;
1517 712         1348 my ($xml, $context) = @_;
1518              
1519 712 50       1514 print ("_canonicalize_xml:\n") if $DEBUG;
1520             my $canon_method = $self->{ parser }->findnodes(
1521 712         1893 'dsig:SignedInfo/dsig:CanonicalizationMethod', $context
1522             );
1523              
1524 712         29973 foreach my $node ($canon_method->get_nodelist) {
1525 712         4589 my $alg = $node->getAttribute('Algorithm');
1526              
1527 712 50       9137 print (" Canon Method: $alg\n") if $DEBUG;
1528 712 100       3520 if ($alg eq TRANSFORM_C14N) {
    100          
    50          
    50          
    50          
    0          
1529 1 50       3 print (" toStringC14N\n") if $DEBUG;
1530 1         5 $xml = $xml->toStringC14N();
1531             }
1532             elsif ($alg eq TRANSFORM_C14N_COMMENTS) {
1533 4 50       50 print (" toStringC14N_Comments\n") if $DEBUG;
1534 4         26 $xml = $xml->toStringC14N(1);
1535             }
1536             elsif ($alg eq TRANSFORM_C14N_V1_1) {
1537 0 0       0 print (" toStringC14N_v1_1\n") if $DEBUG;
1538 0         0 $xml = $xml->toStringC14N_v1_1();
1539             }
1540             elsif ($alg eq TRANSFORM_C14N_V1_1_COMMENTS) {
1541 0 0       0 print (" toStringC14N_v1_1_Comments\n") if $DEBUG;
1542 0         0 $xml = $xml->toStringC14N_v1_1(1);
1543             }
1544             elsif ($alg eq TRANSFORM_EXC_C14N) {
1545 707 50       1340 print (" toStringEC14N\n") if $DEBUG;
1546 707         1825 $xml = $xml->toStringEC14N();
1547             }
1548             elsif ($alg eq TRANSFORM_EXC_C14N_COMMENTS) {
1549 0 0       0 print (" toStringEC14N_Comments\n") if $DEBUG;
1550 0         0 $xml = $xml->toStringEC14N(1);
1551             }
1552             else {
1553 0         0 die "Unsupported transform: $alg";
1554             }
1555             }
1556 712         116639 return $xml;
1557             }
1558              
1559             ##
1560             ## _calc_dsa_signature($signed_info_canon)
1561             ##
1562             ## Arguments:
1563             ## $canonical: string Canonical XML
1564             ##
1565             ## Returns: string Signature
1566             ##
1567             ## Calculates signature based on the method and hash
1568             ##
1569             sub _calc_dsa_signature {
1570 58     58   100 my $self = shift;
1571 58         96 my $signed_info_canon = shift;
1572              
1573 58 50       136 print (" Signing SignedInfo using DSA key type\n") if $DEBUG;
1574 58 50       347 if(my $ref = Digest::SHA->can($self->{ sig_hash })) {
    0          
1575 58         139 $self->{sig_method} = $ref;
1576             }
1577             elsif ( $ref = Crypt::Digest::RIPEMD160->can($self->{ sig_hash })) {
1578 0         0 $self->{sig_method} = $ref;
1579             }
1580             else {
1581 0         0 die("Can't handle $self->{ sig_hash }");
1582             }
1583              
1584             # DSA 1024-bit only permits the signing of 20 bytes or less, hence the sha1
1585             # DSA 2048-bit only permits the signing sha256
1586 58         54552 my $bin_signature = $self->{key_obj}->do_sign( $self->{ sig_method }($signed_info_canon) );
1587              
1588             # https://www.w3.org/TR/2002/REC-xmldsig-core-20020212/#sec-SignatureAlg
1589             # The output of the DSA algorithm consists of a pair of integers
1590             # The signature value consists of the base64 encoding of the
1591             # concatenation of r and s in that order ($r . $s)
1592 58         362 my $r = $bin_signature->get_r;
1593 58         172 my $s = $bin_signature->get_s;
1594              
1595 58         280 my $sig_size = ($self->{key_obj}->get_sig_size - 8) * 8;
1596 58         212 my $rs = _zero_fill_buffer($sig_size);
1597 58         215 _concat_dsa_sig_r_s(\$rs, $r, $s, $sig_size);
1598              
1599 58         455 return $rs;
1600              
1601             }
1602              
1603             ##
1604             ## _calc_ecdsa_signature($signed_info_canon)
1605             ##
1606             ## Arguments:
1607             ## $canonical: string Canonical XML
1608             ##
1609             ## Returns: string Signature
1610             ##
1611             ## Calculates signature based on the method and hash
1612             ##
1613             sub _calc_ecdsa_signature {
1614 172     172   334 my $self = shift;
1615 172         293 my $signed_info_canon = shift;
1616              
1617 172 50       412 print (" Signing SignedInfo using ECDSA key type\n") if $DEBUG;
1618              
1619             my $bin_signature = $self->{key_obj}->sign_message_rfc7518(
1620             $signed_info_canon, uc($self->{sig_hash})
1621 172         953284 );
1622             # The output of the ECDSA algorithm consists of a pair of integers
1623             # The signature value consists of the base64 encoding of the
1624             # concatenation of r and s in that order ($r . $s). In this
1625             # case sign_message_rfc7518 produces that
1626 172         1500 return $bin_signature;
1627             }
1628              
1629             ##
1630             ## _calc_rsa_signature($signed_info_canon)
1631             ##
1632             ## Arguments:
1633             ## $canonical: string Canonical XML
1634             ##
1635             ## Returns: string Signature
1636             ##
1637             ## Calculates signature based on the method and hash
1638             ##
1639             sub _calc_rsa_signature {
1640 109     109   184 my $self = shift;
1641 109         209 my $signed_info_canon = shift;
1642              
1643 109 50       239 print (" Signing SignedInfo using RSA key type\n") if $DEBUG;
1644 109         305 my $sig_hash = 'use_' . $self->{ sig_hash } . '_hash';
1645 109         568 $self->{key_obj}->$sig_hash;
1646 109         288574 my $bin_signature = $self->{key_obj}->sign( $signed_info_canon );
1647              
1648 109         895 return $bin_signature;
1649             }
1650              
1651             ##
1652             ## _calc_hmac_signature($signed_info_canon)
1653             ##
1654             ## Arguments:
1655             ## $signed_info_canon: string Canonical XML
1656             ##
1657             ## Returns: string Signature
1658             ##
1659             ## Calculates signature based on the method and hash
1660             ##
1661             sub _calc_hmac_signature {
1662 18     18   27 my $self = shift;
1663 18         29 my $signed_info_canon = shift;
1664              
1665 23     23   255 use Crypt::Mac::HMAC qw( hmac );
  23         73  
  23         6458  
1666 18         26 my $bin_signature;
1667 18 50       35 print (" Signing SignedInfo using hmac-", $self->{ sig_hash }, "\n") if $DEBUG;
1668 18 100       181 if (my $ref = Digest::SHA->can('hmac_' . $self->{ sig_hash })) {
    50          
1669 15         43 $self->{sig_method} = $ref;
1670             $bin_signature = $self->{sig_method} (
1671             $signed_info_canon,
1672             decode_base64( $self->{ hmac_key } )
1673 15         367 );
1674             }
1675             elsif ( $ref = Crypt::Digest::RIPEMD160->can($self->{ sig_hash })) {
1676 3         51 $self->{sig_method} = $ref;
1677 3         77 $bin_signature = hmac('RIPEMD160', decode_base64( $self->{ hmac_key } ), $signed_info_canon );
1678             }
1679             else {
1680 0         0 die("Can't handle $self->{ sig_hash }");
1681             }
1682              
1683 18         79 return $bin_signature;
1684             }
1685             1;
1686              
1687             =pod
1688              
1689             =encoding UTF-8
1690              
1691             =head1 NAME
1692              
1693             XML::Sig - XML::Sig - A toolkit to help sign and verify XML Digital Signatures
1694              
1695             =head1 VERSION
1696              
1697             version 0.63
1698              
1699             =head1 SYNOPSIS
1700              
1701             my $xml = '<foo ID="abc">123</foo>';
1702             my $signer = XML::Sig->new({
1703             key => 'path/to/private.key',
1704             });
1705              
1706             # create a signature
1707             my $signed = $signer->sign($xml);
1708             print "Signed XML: $signed\n";
1709              
1710             # verify a signature
1711             $signer->verify($signed)
1712             or die "Signature Invalid.";
1713             print "Signature valid.\n";
1714              
1715             =head1 DESCRIPTION
1716              
1717             This perl module provides two primary capabilities: given an XML string, create
1718             and insert digital signatures, or if one is already present in the string verify
1719             it -- all in accordance with the W3C standard governing XML signatures.
1720              
1721             =head1 NAME
1722              
1723             XML::Sig - A toolkit to help sign and verify XML Digital Signatures.
1724              
1725             =head1 PREREQUISITES
1726              
1727             =over
1728              
1729             =item * L<Digest::SHA>
1730              
1731             =item * L<XML::LibXML>
1732              
1733             =item * L<MIME::Base64>
1734              
1735             =item * L<Crypt::OpenSSL::X509>
1736              
1737             =item * L<Crypt::OpenSSL::Bignum>
1738              
1739             =item * L<Crypt::OpenSSL::RSA>
1740              
1741             =item * L<Crypt::OpenSSL::DSA>
1742              
1743             =item * L<Crypt::PK::ECC>
1744              
1745             =back
1746              
1747             =head1 USAGE
1748              
1749             =head2 SUPPORTED ALGORITHMS & TRANSFORMS
1750              
1751             This module supports the following signature methods:
1752              
1753             =over
1754              
1755             =item * DSA
1756              
1757             =item * RSA
1758              
1759             =item * RSA encoded as x509
1760              
1761             =item * ECDSA
1762              
1763             =item * ECDSA encoded as x509
1764              
1765             =item * HMAC
1766              
1767             =back
1768              
1769             This module supports the following canonicalization methods and transforms:
1770              
1771             =over
1772              
1773             =item * Enveloped Signature
1774              
1775             =item * REC-xml-c14n-20010315#
1776              
1777             =item * REC-xml-c14n-20010315#WithComments
1778              
1779             =item * REC-xml-c14n11-20080502
1780              
1781             =item * REC-xml-c14n11-20080502#WithComments
1782              
1783             =item * xml-exc-c14n#
1784              
1785             =item * xml-exc-c14n#WithComments
1786              
1787             =back
1788              
1789             =head2 OPTIONS
1790              
1791             Each of the following options are also accessors on the main
1792             XML::Sig object. TODO Not strictly correct rewrite
1793              
1794             =over
1795              
1796             =item B<key>
1797              
1798             The path to a file containing the contents of a private key. This option
1799             is used only when generating signatures.
1800              
1801             =item B<cert>
1802              
1803             The path to a file containing a PEM-formatted X509 certificate. This
1804             option is used only when generating signatures with the "x509"
1805             option. This certificate will be embedded in the signed document, and
1806             should match the private key used for the signature.
1807              
1808             =item B<cert_text>
1809              
1810             A string containing a PEM-formatted X509 certificate. This
1811             option is used only when generating signatures with the "x509"
1812             option. This certificate will be embedded in the signed document, and
1813             should match the private key used for the signature.
1814              
1815             =item B<x509>
1816              
1817             Takes a true (1) or false (0) value and indicates how you want the
1818             signature to be encoded. When true, the X509 certificate supplied will
1819             be encoded in the signature. Otherwise the native encoding format for
1820             RSA, DSA and ECDSA will be used.
1821              
1822             =item B<sig_hash>
1823              
1824             Passing sig_hash to new allows you to specify the SignatureMethod
1825             hashing algorithm used when signing the SignedInfo. RSA and ECDSA
1826             supports the hashes specified sha1, sha224, sha256, sha384 and sha512
1827              
1828             DSA supports only sha1 and sha256 (but you really should not sign
1829             anything with DSA anyway). This is over-ridden by the key's signature
1830             size which is related to the key size. 1024-bit keys require sha1,
1831             2048-bit and 3072-bit keys require sha256.
1832              
1833             =item B<digest_hash>
1834              
1835             Passing digest_hash to new allows you to specify the DigestMethod
1836             hashing algorithm used when calculating the hash of the XML being
1837             signed. Supported hashes can be specified sha1, sha224, sha256,
1838             sha384, sha512, ripemd160
1839              
1840             =item B<hmac_key>
1841              
1842             Base64 encoded hmac_key
1843              
1844             =item B<key_name>
1845              
1846             The name of the key that should be referenced. In the case of
1847             xmlsec the --keys-file (ex. t/xmlsec-keys.xml) holds keys with a
1848             KeyName that is referenced by this name.
1849              
1850             =item B<no_xml_declaration>
1851              
1852             Some applications such as Net::SAML2 expect to sign a fragment of the
1853             full XML document so is this is true (1) it will not include the
1854             XML Declaration at the beginning of the signed XML. False (0) or
1855             undefined returns an XML document starting with the XML Declaration.
1856              
1857             =back
1858              
1859             The following options act similar to C<< xmlsec --id-attr:ID
1860             <node-namespace-uri>:<name> >>
1861              
1862             =over
1863              
1864             =item B<ns>
1865              
1866             A HashRef to namespaces you want to define to select the correct attribute ID on
1867              
1868             =item B<id_attr>
1869              
1870             The xpath string you want to sign your XML message on.
1871              
1872             =back
1873              
1874             =head2 METHODS
1875              
1876             =head3 B<new(...)>
1877              
1878             Constructor; see OPTIONS above.
1879              
1880             =head3 B<sign($xml)>
1881              
1882             When given a string of XML, it will return the same string with a signature
1883             generated from the key provided when the XML::Sig object was initialized.
1884              
1885             This method will sign all elements in your XML with an ID (case sensitive)
1886             attribute. Each element with an ID attribute will be the basis for a seperate
1887             signature. It will correspond to the URI attribute in the Reference element
1888             that will be contained by the signature. If no ID attribute can be found on
1889             an element, the signature will not be created.
1890              
1891             The elements are signed in reverse order currently assuming (possibly
1892             incorrectly) that the lower element in the tree may need to be signed
1893             inclusive of its Signature because it is a child of the higher element.
1894              
1895             Arguments:
1896             $xml: string XML string
1897              
1898             Returns: string Signed XML
1899              
1900             =head3 B<verify($xml)>
1901              
1902             Returns true or false based upon whether the signature is valid or not.
1903              
1904             When using XML::Sig exclusively to verify a signature, no key needs to be
1905             specified during initialization given that the public key should be
1906             transmitted with the signature.
1907              
1908             XML::Sig checks all signature in the provided xml and will fail should any
1909             signature pointing to an existing ID in the XML fail to verify.
1910              
1911             Should there be a Signature included that does not point to an existing node
1912             in the XML it is ignored and other Signaures are checked. If there are no
1913             other Signatures it will return false.
1914              
1915             Arguments:
1916             $xml: string XML string
1917              
1918             Returns: string Signed XML
1919              
1920             =head3 B<signer_cert()>
1921              
1922             Following a successful verify with an X509 certificate, returns the
1923             signer's certificate as embedded in the XML document for verification
1924             against a CA certificate. The certificate is returned as a
1925             Crypt::OpenSSL::X509 object.
1926              
1927             Arguments: none
1928              
1929             Returns: Crypt::OpenSSL::X509: Certificate used to sign the XML
1930              
1931             =head1 ABOUT DIGITAL SIGNATURES
1932              
1933             Just as one might want to send an email message that is cryptographically signed
1934             in order to give the recipient the means to independently verify who sent the email,
1935             one might also want to sign an XML document. This is especially true in the
1936             scenario where an XML document is received in an otherwise unauthenticated
1937             context, e.g. SAML.
1938              
1939             However XML provides a challenge that email does not. In XML, two documents can be
1940             byte-wise inequivalent, and semanticaly equivalent at the same time. For example:
1941              
1942             <?xml version="1.0"?>
1943             <foo>
1944             <bar />
1945             </foo>
1946              
1947             And:
1948              
1949             <?xml version="1.0"?>
1950             <foo>
1951             <bar></bar>
1952             </foo>
1953              
1954             Each of these document express the same thing, or in other words they "mean"
1955             the same thing. However if you were to strictly sign the raw text of these
1956             documents, they would each produce different signatures.
1957              
1958             XML Signatures on the other hand will produce the same signature for each of
1959             the documents above. Therefore an XML document can be written and rewritten by
1960             different parties and still be able to have someone at the end of the line
1961             verify a signature the document may contain.
1962              
1963             There is a specially subscribed methodology for how this process should be
1964             executed and involves transforming the XML into its canonical form so a
1965             signature can be reliably inserted or extracted for verification. This
1966             module implements that process.
1967              
1968             =head2 EXAMPLE SIGNATURE
1969              
1970             Below is a sample XML signature to give you some sense of what they look like.
1971             First let's look at the original XML document, prior to being signed:
1972              
1973             <?xml version="1.0"?>
1974             <foo ID="abc">
1975             <bar>123</bar>
1976             </foo>
1977              
1978             Now, let's insert a signature:
1979              
1980             <?xml version="1.0"?>
1981             <foo ID="abc">
1982             <bar>123</bar>
1983             <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
1984             <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
1985             <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" />
1986             <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
1987             <Reference URI="#abc">
1988             <Transforms>
1989             <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
1990             </Transforms>
1991             <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
1992             <DigestValue>9kpmrvv3peVJpNSTRycrV+jeHVY=</DigestValue>
1993             </Reference>
1994             </SignedInfo>
1995             <SignatureValue>
1996             HXUBnMgPJf//j4ihaWnaylNwAR5AzDFY83HljFIlLmTqX1w1C72ZTuRObvYve8TNEbVsQlTQkj4R
1997             hiY0pgIMQUb75GLYFtc+f0YmBZf5rCWY3NWzo432D3ogAvpEzYXEQPmicWe2QozQhybaz9/wrYki
1998             XiXY+57fqCkf7aT8Bb6G+fn7Aj8gnZFLkmKxwCdyGsIZOIZdQ8MWpeQrifxBR0d8W1Zm6ix21WNv
1999             ONt575h7VxLKw8BDhNPS0p8CS3hOnSk29stpiDMCHFPxAwrbKVL1kGDLaLZn1q8nNRmH8oFxG15l
2000             UmS3JXDZAss8gZhU7g9T4XllCqjrAvzPLOFdeQ==
2001             </SignatureValue>
2002             <KeyInfo>
2003             <KeyValue>
2004             <RSAKeyValue>
2005             <Modulus>
2006             1b+m37u3Xyawh2ArV8txLei251p03CXbkVuWaJu9C8eHy1pu87bcthi+T5WdlCPKD7KGtkKn9vq
2007             i4BJBZcG/Y10e8KWVlXDLg9gibN5hb0Agae3i1cCJTqqnQ0Ka8w1XABtbxTimS1B0aO1zYW6d+U
2008             Yl0xIeAOPsGMfWeu1NgLChZQton1/NrJsKwzMaQy1VI8m4gUleit9Z8mbz9bNMshdgYEZ9oC4bH
2009             n/SnA4FvQl1fjWyTpzL/aWF/bEzS6Qd8IBk7yhcWRJAGdXTWtwiX4mXb4h/2sdrSNvyOsd/shCf
2010             OSMsf0TX+OdlbH079AsxOwoUjlzjuKdCiFPdU6yAJw==
2011             </Modulus>
2012             <Exponent>Iw==</Exponent>
2013             </RSAKeyValue>
2014             </KeyValue>
2015             </KeyInfo>
2016             </Signature>
2017             </foo>
2018              
2019             =head1 SEE ALSO
2020              
2021             L<http://www.w3.org/TR/xmldsig-core/>
2022              
2023             =head1 VERSION CONTROL
2024              
2025             L<https://github.com/perl-net-saml2/perl-XML-Sig>
2026              
2027             =head1 AUTHORS and CREDITS
2028              
2029             Author: Byrne Reese <byrne@majordojo.com>
2030              
2031             Thanks to Manni Heumann who wrote Google::SAML::Response from
2032             which this module borrows heavily in order to create digital
2033             signatures.
2034              
2035             Net::SAML2 embedded version amended by Chris Andrews <chris@nodnol.org>.
2036              
2037             Maintainer: Timothy Legge <timlegge@cpan.org>
2038              
2039             =head1 AUTHORS
2040              
2041             =over 4
2042              
2043             =item *
2044              
2045             Byrne Reese <byrne@cpan.org>
2046              
2047             =item *
2048              
2049             Timothy Legge <timlegge@cpan.org>
2050              
2051             =back
2052              
2053             =head1 COPYRIGHT AND LICENSE
2054              
2055             This software is copyright (c) 2023 by Byrne Reese, Chris Andrews and Others; in detail:
2056              
2057             Copyright 2009 Byrne, Michael Hendricks
2058             2010 Chris Andrews
2059             2011 Chris Andrews, Oskari Okko Ojala
2060             2012 Chris Andrews, Peter Marschall
2061             2015 Mike Wisener
2062             2016 Jeff Fearn
2063             2017 Mike Wisener, xmikew
2064             2019-2021 Timothy Legge
2065             2022 Timothy Legge, Wesley Schwengle
2066             2023 Timothy Legge
2067              
2068              
2069             This is free software; you can redistribute it and/or modify it under
2070             the same terms as the Perl 5 programming language system itself.
2071              
2072             =cut
2073              
2074             __END__
2075              
2076             }