File Coverage

blib/lib/XML/Sig.pm
Criterion Covered Total %
statement 592 658 89.9
branch 180 308 58.4
condition 32 45 71.1
subroutine 56 58 96.5
pod 4 4 100.0
total 864 1073 80.5


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