File Coverage

blib/lib/XML/Sig.pm
Criterion Covered Total %
statement 597 666 89.6
branch 191 308 62.0
condition 33 48 68.7
subroutine 56 60 93.3
pod 4 4 100.0
total 881 1086 81.1


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