File Coverage

blib/lib/XML/Enc.pm
Criterion Covered Total %
statement 394 431 91.4
branch 68 108 62.9
condition 14 19 73.6
subroutine 52 54 96.3
pod 3 3 100.0
total 531 615 86.3


line stmt bran cond sub pod time code
1 9     9   1056466 use strict;
  9         81  
  9         290  
2 9     9   46 use warnings;
  9         17  
  9         475  
3              
4             package XML::Enc;
5             our $VERSION = '0.12'; # VERSION
6              
7             # ABSTRACT: XML::Enc Encryption Support
8              
9 9     9   49 use Carp;
  9         13  
  9         622  
10 9     9   4239 use Crypt::AuthEnc::GCM 0.062;
  9         28651  
  9         430  
11 9     9   4014 use Crypt::Mode::CBC;
  9         8270  
  9         259  
12 9     9   4921 use Crypt::PK::RSA;
  9         101212  
  9         443  
13 9     9   65 use Crypt::PRNG qw( random_bytes );
  9         15  
  9         416  
14 9     9   4217 use MIME::Base64 qw/decode_base64 encode_base64/;
  9         6051  
  9         563  
15 9     9   6063 use XML::LibXML;
  9         445424  
  9         61  
16              
17             # state means perl 5.10
18 9     9   1428 use feature 'state';
  9         22  
  9         1196  
19 9     9   71 use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG);
  9         34  
  9         47438  
20              
21             our $DEBUG = 0;
22              
23              
24             # Source: https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#sec-Alg-Block
25             # 5.2.1 Triple DES - 64 bit Initialization Vector (IV) (8 bytes)
26             # 5.2.2 AES - 128 bit initialization vector (IV) (16 bytes)
27              
28             sub _assert_symmetric_algorithm {
29 113     113   174 my $algo = shift;
30              
31 113         312 state $SYMMETRIC = {
32             'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => {
33             ivsize => 8,
34             keysize => 24,
35             modename => 'DES_EDE'
36             },
37             'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => {
38             ivsize => '16',
39             keysize => 16,
40             modename => 'AES'
41             },
42             'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => {
43             ivsize => '16',
44             keysize => 24,
45             modename => 'AES'
46             },
47             'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => {
48             ivsize => '16',
49             keysize => 32,
50             modename => 'AES'
51             },
52             'http://www.w3.org/2009/xmlenc11#aes128-gcm' => {
53             ivsize => '12',
54             keysize => 16,
55             modename => 'AES',
56             tagsize => 16
57             },
58             'http://www.w3.org/2009/xmlenc11#aes192-gcm' => {
59             ivsize => '12',
60             keysize => 24,
61             modename => 'AES',
62             tagsize => 16
63             },
64             'http://www.w3.org/2009/xmlenc11#aes256-gcm' => {
65             ivsize => '12',
66             keysize => 32,
67             modename => 'AES',
68             tagsize => 16
69             },
70             };
71              
72 113 50       488 die "Unsupported symmetric algo $algo" unless $SYMMETRIC->{ $algo };
73 113         232 return $SYMMETRIC->{$algo}
74             }
75              
76             sub _assert_encryption_digest {
77 2     2   8 my $algo = shift;
78              
79 2         11 state $ENC_DIGEST = {
80             'http://www.w3.org/2000/09/xmldsig#sha1' => 'SHA1',
81             'http://www.w3.org/2001/04/xmlenc#sha256' => 'SHA256',
82             };
83              
84 2 50       11 die "Unsupported encryption digest algo $algo" unless $ENC_DIGEST->{ $algo };
85 2         6 return $ENC_DIGEST->{ $algo };
86             }
87              
88              
89              
90             sub new {
91 64     64 1 85247 my $class = shift;
92 64         210 my $params = shift;
93 64         116 my $self = {};
94              
95 64         134 bless $self, $class;
96              
97 64 50       204 if ( exists $params->{ 'key' } ) {
98 64         191 $self->{key} = $params->{ 'key' };
99 64         180 $self->_load_key( $params->{ 'key' } );
100             }
101 64 100       237 if ( exists $params->{ 'cert' } ) {
102 52         117 $self->{cert} = $params->{ 'cert' };
103 52         141 $self->_load_cert_file( $params->{ 'cert' } );
104             }
105 64 50       216 if (exists $params->{'no_xml_declaration'}) {
106 64 50       218 $self->{'no_xml_declaration'} = $params->{'no_xml_declaration'} ? $params->{'no_xml_declaration'} : 0;
107             }
108              
109 64 100       169 my $enc_method = exists($params->{'data_enc_method'}) ? $params->{'data_enc_method'} : 'aes256-cbc';
110 64         190 $self->{'data_enc_method'} = $self->_setEncryptionMethod($enc_method);
111              
112 64 100       169 my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
113 64         171 $self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);
114              
115 64 100       151 my $oaep_mgf_alg = exists($params->{'oaep_mgf_alg'}) ? $params->{'oaep_mgf_alg'} : 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
116 64         154 $self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($oaep_mgf_alg);
117              
118 64 100       167 $self->{'oaep_params'} = exists($params->{'oaep_params'}) ? $params->{'oaep_params'} : '';
119              
120 64         275 return $self;
121             }
122              
123              
124             sub decrypt {
125 64     64 1 44723 my $self = shift;
126 64         133 my $xml = shift;
127 64         127 my %options = @_;
128              
129 64         127 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
130              
131 64         251 my $doc = XML::LibXML->load_xml( string => $xml );
132              
133 63         18778 my $xpc = XML::LibXML::XPathContext->new($doc);
134 63         439 $xpc->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
135 63         217 $xpc->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
136 63         188 $xpc->registerNs('xenc11', 'http://www.w3.org/2009/xmlenc11#');
137 63         197 $xpc->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
138              
139 63 50       221 return $doc unless $xpc->exists('//xenc:EncryptedData');
140              
141 63 50       3040 die "You cannot decrypt XML without a private key." unless $self->{key_obj};
142              
143 63         206 my $parser = XML::LibXML->new();
144 63         943 $self->_decrypt_encrypted_key_nodes($xpc, $parser, %options);
145 63         2651 $self->_decrypt_uri_nodes($xpc, $parser, %options);
146              
147 63         239 return $doc->serialize();
148             }
149              
150             sub _decrypt_encrypted_key_nodes {
151 63     63   104 my $self = shift;
152 63         86 my $xpc = shift;
153 63         91 my $parser = shift;
154 63         111 my %options = @_;
155              
156             my $k = $self->_get_named_key_nodes(
157             '//xenc:EncryptedData/dsig:KeyInfo/xenc:EncryptedKey',
158             $xpc, $options{key_name}
159 63         236 );
160              
161             $k->foreach(
162             sub {
163 62     62   768 my $key = $self->_get_key_from_node($_, $xpc);
164 62 100       190 return unless $key;
165 61         581 my $encrypted_node = $_->parentNode->parentNode;
166 61         307 $self->_decrypt_encrypted_node($encrypted_node,
167             $key, $xpc, $parser);
168             }
169 63         423 );
170             }
171              
172             sub _decrypt_uri_nodes {
173 63     63   106 my $self = shift;
174 63         82 my $xpc = shift;
175 63         82 my $parser = shift;
176 63         108 my %options = @_;
177              
178 63         205 my $uri_nodes = $xpc->findnodes('//dsig:KeyInfo/dsig:RetrievalMethod/@URI');
179 63     1   3202 my @uri_nodes = $uri_nodes->map(sub { my $v = $_->getValue; $v =~ s/^#//; return $v; });
  1         21  
  1         10  
  1         7  
180              
181 63         1333 foreach my $uri (@uri_nodes) {
182             my $encrypted_key_nodes = $self->_get_named_key_nodes(
183             sprintf('//xenc:EncryptedKey[@Id="%s"]', $uri),
184 1         12 $xpc, $options{key_name});
185              
186             $encrypted_key_nodes->foreach(
187             sub {
188              
189 1     1   15 my $key = $self->_get_key_from_node($_, $xpc);
190 1 50       7 return unless $key;
191              
192 1         23 my $encrypted_nodes = $xpc->findnodes(sprintf('//dsig:KeyInfo/dsig:RetrievalMethod[@URI="#%s"]/../..', $uri));
193 1 50       111 return unless $encrypted_nodes->size;
194              
195             $encrypted_nodes->foreach(sub {
196 1         17 $self->_decrypt_encrypted_node(
197             $_,
198             $key,
199             $xpc,
200             $parser
201             );
202 1         20 });
203              
204             # We don't need the encrypted key here
205 1         28 $_->removeChildNodes();
206             }
207 1         9 );
208             }
209             }
210              
211             sub _get_named_key_nodes {
212 64     64   122 my $self = shift;
213 64         88 my $xpath = shift;
214 64         102 my $xpc = shift;
215 64         138 my $name = shift;
216              
217 64         157 my $nodes = $xpc->findnodes($xpath);
218 64 50       2825 return $nodes unless $name;
219             return $nodes->grep(
220             sub {
221 0     0   0 $xpc->findvalue('dsig:KeyInfo/dsig:KeyName', $_) eq $name;
222             }
223 0         0 );
224             }
225              
226             sub _decrypt_encrypted_node {
227 62     62   1313 my $self = shift;
228 62         105 my $node = shift;
229 62         96 my $key = shift;
230 62         94 my $xpc = shift;
231 62         99 my $parser = shift;
232              
233 62         170 my $algo = $self->_get_encryption_algorithm($node, $xpc);
234 62         1240 my $cipher_value = $self->_get_cipher_value($node, $xpc);
235 62         2050 my $oaep = $self->_get_oaep_params($node, $xpc);
236              
237 62         197 my $decrypted_data = $self->_DecryptData($algo, $key, $cipher_value);
238              
239             # Sooo.. parse_balanced_chunk breaks when there is a 240             # bit in the decrypted data and thus we have to remove it.
241             # We try parsing the XML here and if that works we get all the nodes
242 62         117 my $fragment;
243 62         97 eval {
244 62         211 $fragment = $parser->load_xml(string => $decrypted_data)->findnodes('//*')->[0];
245             };
246 62   66     21994 $fragment = $parser->parse_balanced_chunk($fragment // $decrypted_data);
247              
248 62 50 100     18297 if (($node->parentNode->localname //'') eq 'EncryptedID') {
249 0         0 $node->parentNode->replaceNode($fragment);
250 0         0 return;
251             }
252 62         765 $node->replaceNode($fragment);
253 62         455 return;
254             }
255              
256             sub _get_key_from_node {
257 63     63   82 my $self = shift;
258 63         88 my $node = shift;
259 63         96 my $xpc = shift;
260              
261 63         134 my $algo = $self->_get_encryption_algorithm($_, $xpc);
262 63         1192 my $cipher_value = $self->_get_cipher_value($_, $xpc);
263 63         2434 my $digest_name = $self->_get_digest_method($_, $xpc);
264 63         160 my $oaep = $self->_get_oaep_params($_, $xpc);
265 63         207 my $mgf = $self->_get_mgf($_, $xpc);
266              
267 63         187 return $self->_decrypt_key(
268             $cipher_value,
269             $algo,
270             $digest_name,
271             $oaep,
272             $mgf,
273             );
274             }
275              
276             sub _get_encryption_algorithm {
277 125     125   216 my $self = shift;
278 125         178 my $node = shift;
279 125         156 my $xpc = shift;
280              
281 125         335 my $nodes = $xpc->findnodes('./xenc:EncryptionMethod/@Algorithm', $node);
282 125 50       6593 return $nodes->get_node(1)->getValue if $nodes->size;
283 0         0 confess "Unable to determine encryption method algorithm from " . $node->nodePath;
284             }
285              
286             sub _get_cipher_value {
287 125     125   187 my $self = shift;
288 125         165 my $node = shift;
289 125         170 my $xpc = shift;
290              
291 125         261 my $nodes = $xpc->findnodes('./xenc:CipherData/xenc:CipherValue', $node);
292 125 50       4829 return decode_base64($nodes->get_node(1)->textContent) if $nodes->size;
293 0         0 confess "Unable to get the CipherValue from " . $node->nodePath;
294             }
295              
296             sub _get_mgf {
297 63     63   96 my $self = shift;
298 63         80 my $node = shift;
299 63         92 my $xpc = shift;
300              
301 63         127 my $value = $xpc->findvalue('./xenc:EncryptionMethod/xenc11:MGF/@Algorithm', $node);
302 63 100       3714 return $value if $value;
303 28         73 return;
304             }
305              
306             sub _get_oaep_params {
307 125     125   174 my $self = shift;
308 125         193 my $node = shift;
309 125         152 my $xpc = shift;
310              
311 125         272 my $value = $xpc->findvalue('./xenc:EncryptionMethod/xenc:OAEPparams', $node);
312 125 100       7277 return decode_base64($value) if $value;
313 122         255 return;
314             }
315              
316             sub _get_digest_method {
317 63     63   108 my $self = shift;
318 63         109 my $node = shift;
319 63         91 my $xpc = shift;
320              
321 63         143 my $value = $xpc->findvalue(
322             './xenc:EncryptionMethod/dsig:DigestMethod/@Algorithm', $node);
323 63 100       4053 return _assert_encryption_digest($value) if $value;
324 61         139 return;
325             }
326              
327              
328             sub encrypt {
329 51     51 1 230 my $self = shift;
330 51         118 my ($xml) = @_;
331              
332 51         93 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
333              
334             # Create the EncryptedData node
335 51         106 my ($encrypted) = $self->_create_encrypted_data_xml();
336              
337 51         4176 my $dom = XML::LibXML->load_xml( string => $xml);
338              
339 51         12658 my $xpc = XML::LibXML::XPathContext->new($encrypted);
340 51         360 $xpc->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
341 51         166 $xpc->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
342 51         152 $xpc->registerNs('xenc11', 'http://www.w3.org/2009/xmlenc11#');
343 51         165 $xpc->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
344              
345             # Encrypt the data an empty key is passed by reference to allow
346             # the key to be generated at the same time the data is being encrypted
347 51         65 my $key;
348 51         111 my $method = $self->{data_enc_method};
349 51         161 my $encrypteddata = $self->_EncryptData ($method, $dom->serialize(), \$key);
350              
351             # Encrypt the Key immediately after the data is encrypted. It is passed by
352             # reference to reduce the number of times that the unencrypted key is
353             # stored in memory
354 51         1385 $self->_EncryptKey($self->{key_transport}, \$key);
355              
356 51         270 my $base64_key = encode_base64($key);
357 51         134 my $base64_data = encode_base64($encrypteddata);
358              
359             # Insert OAEPparams into the XML
360 51 100       128 if ($self->{oaep_params} ne '') {
361 1         6 $encrypted = $self->_setOAEPparams($encrypted, $xpc, encode_base64($self->{oaep_params}));
362             }
363              
364             # Insert Encrypted data into XML
365 51         138 $encrypted = $self->_setEncryptedData($encrypted, $xpc, $base64_data);
366              
367             # Insert the Encrypted Key into the XML
368 51         764 $self->_setKeyEncryptedData($encrypted, $xpc, $base64_key);
369              
370 51         783 return $encrypted->serialize();
371             }
372              
373             sub _setEncryptionMethod {
374 64     64   102 my $self = shift;
375 64         89 my $method = shift;
376              
377 64         386 my %methods = (
378             'aes128-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
379             'aes192-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes192-cbc',
380             'aes256-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
381             'tripledes-cbc' => 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
382             'aes128-gcm' => 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
383             'aes192-gcm' => 'http://www.w3.org/2009/xmlenc11#aes192-gcm',
384             'aes256-gcm' => 'http://www.w3.org/2009/xmlenc11#aes256-gcm',
385             );
386              
387 64 50       356 return exists($methods{$method}) ? $methods{$method} : $methods{'aes256-cbc'};
388             }
389              
390             sub _setOAEPparams {
391 1     1   3 my $self = shift;
392 1         1 my $context = shift;
393 1         2 my $xpc = shift;
394 1         5 my $oaep_params = shift;
395              
396 1         4 my $node = $xpc->findnodes('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
397              
398 1         76 $node->[0]->removeChildNodes();
399 1         7 $node->[0]->appendText($oaep_params);
400 1         3 return $context;
401             }
402              
403             sub _setOAEPAlgorithm {
404 64     64   109 my $self = shift;
405 64         94 my $method = shift;
406              
407 64         123 state $setOAEPAlgorithm = {
408             'mgf1sha1' => 'http://www.w3.org/2009/xmlenc11#mgf1sha1',
409             'mgf1sha224' => 'http://www.w3.org/2009/xmlenc11#mgf1sha224',
410             'mgf1sha256' => 'http://www.w3.org/2009/xmlenc11#mgf1sha256',
411             'mgf1sha384' => 'http://www.w3.org/2009/xmlenc11#mgf1sha384',
412             'mgf1sha512' => 'http://www.w3.org/2009/xmlenc11#mgf1sha512',
413             };
414              
415 64   66     217 return $setOAEPAlgorithm->{$method} // $setOAEPAlgorithm->{'rsa-oaep-mgf1p'};
416             }
417              
418             sub _getOAEPAlgorithm {
419 70     70   94 my $self = shift;
420 70         94 my $method = shift;
421              
422 70         111 state $OAEPAlgorithm = {
423             'http://www.w3.org/2009/xmlenc11#mgf1sha1' => 'SHA1',
424             'http://www.w3.org/2009/xmlenc11#mgf1sha224' => 'SHA224',
425             'http://www.w3.org/2009/xmlenc11#mgf1sha256' => 'SHA256',
426             'http://www.w3.org/2009/xmlenc11#mgf1sha384' => 'SHA384',
427             'http://www.w3.org/2009/xmlenc11#mgf1sha512' => 'SHA512',
428             };
429              
430 70   50     1027624 return $OAEPAlgorithm->{$method} // 'SHA1';
431             }
432              
433             sub _setKeyEncryptionMethod {
434 64     64   99 my $self = shift;
435 64         85 my $method = shift;
436              
437 64         133 state $enc_methods = {
438             'rsa-1_5' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',
439             'rsa-oaep-mgf1p' => 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
440             'rsa-oaep' => 'http://www.w3.org/2009/xmlenc11#rsa-oaep',
441             };
442              
443 64   66     231 return $enc_methods->{$method} // $enc_methods->{'rsa-oaep-mgf1p'};
444             }
445              
446             sub _DecryptData {
447 62     62   103 my $self = shift;
448 62         105 my $method = shift;
449 62         92 my $key = shift;
450 62         95 my $encrypteddata = shift;
451              
452 62         160 my $method_vars = _assert_symmetric_algorithm($method);
453              
454 62         122 my $ivsize = $method_vars->{ivsize};
455 62         110 my $tagsize = $method_vars->{tagsize};
456              
457 62         260 my $iv = substr $encrypteddata, 0, $ivsize;
458 62         156 my $encrypted = substr $encrypteddata, $ivsize;
459              
460             # XML Encryption 5.2 Block Encryption Algorithms
461             # The resulting cipher text is prefixed by the IV.
462 62 100       338 if ($method !~ /gcm/ ){
463 41         390 my $cbc = Crypt::Mode::CBC->new($method_vars->{modename}, 0);
464 41         186 return $self->_remove_padding($cbc->decrypt($encrypted, $key, $iv));
465             }
466              
467 21         4191 my $gcm = Crypt::AuthEnc::GCM->new("AES", $key, $iv);
468              
469             # Note that GCM support for additional authentication
470             # data is not used in the XML specification.
471 21         89 my $tag = substr $encrypted, -$tagsize;
472 21         63 $encrypted = substr $encrypted, 0, (length $encrypted) - $tagsize;
473 21         135 my $plaintext = $gcm->decrypt_add($encrypted);
474              
475 21 50       117 die "Tag expected did not match returned Tag"
476             unless $gcm->decrypt_done($tag);
477              
478 21         103 return $plaintext;
479             }
480              
481             sub _EncryptData {
482 51     51   2699 my $self = shift;
483 51         78 my $method = shift;
484 51         72 my $data = shift;
485 51         84 my $key = shift;
486              
487              
488 51         121 my $method_vars = _assert_symmetric_algorithm($method);
489              
490 51         182 my $ivsize = $method_vars->{ivsize};
491 51         146 my $keysize = $method_vars->{keysize};
492              
493 51         177 my $iv = random_bytes($ivsize);
494 51         907 ${$key} = random_bytes($keysize);
  51         413  
495              
496 51 100       210 if ($method =~ /gcm/ ){
497             my $gcm
498 21         50 = Crypt::AuthEnc::GCM->new($method_vars->{modename}, ${$key}, $iv);
  21         4248  
499              
500             # Note that GCM support for additional authentication
501             # data is not used in the XML specification.
502 21         170 my $encrypted = $gcm->encrypt_add($data);
503 21         86 my $tag = $gcm->encrypt_done();
504              
505 21         135 return $iv . $encrypted . $tag;
506             }
507              
508 30         312 my $cbc = Crypt::Mode::CBC->new($method_vars->{modename}, 0);
509             # XML Encryption 5.2 Block Encryption Algorithms
510             # The resulting cipher text is prefixed by the IV.
511 30         105 $data = $self->_add_padding($data, $ivsize);
512 30         62 return $iv . $cbc->encrypt($data, ${$key}, $iv);
  30         108  
513             }
514              
515             sub _decrypt {
516 63     63   103 my $sub = shift;
517 63         99 my $decrypt;
518 63         109 eval { $decrypt = $sub->() };
  63         148  
519 63 100       1025 return $decrypt unless $@;
520 1         9 return;
521             }
522              
523             sub _decrypt_key {
524 63     63   95 my $self = shift;
525 63         90 my $key = shift;
526 63         88 my $algo = shift;
527 63         102 my $digest_name = shift;
528 63         93 my $oaep = shift;
529 63         84 my $mgf = shift;
530              
531 63 100       166 if ($algo eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
532 12     12   64 return _decrypt(sub{$self->{key_obj}->decrypt($key, 'v1.5')});
  12         343557  
533             }
534              
535 51 100       116 if ($algo eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
536             return _decrypt(
537             sub {
538             $self->{key_obj}->decrypt(
539 16   100 16   458038 $key, 'oaep',
      100        
540             $digest_name // 'SHA1',
541             $oaep // ''
542             );
543             }
544 16         101 );
545             }
546              
547 35 50       74 if ($algo eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
548             return _decrypt(
549             sub {
550             $self->{key_obj}->decrypt(
551 35   50 35   80 $key, 'oaep',
552             $self->_getOAEPAlgorithm($mgf),
553             $oaep // '',
554             );
555             }
556 35         167 );
557             }
558              
559 0         0 die "Unsupported algorithm for key decryption: $algo";
560             }
561              
562             sub _EncryptKey {
563 51     51   85 my $self = shift;
564 51         66 my $keymethod = shift;
565 51         63 my $key = shift;
566              
567 51         87 my $rsa_pub = $self->{cert_obj};
568              
569 51 100       185 if ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
    100          
    50          
570 7         13 ${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
  7         32  
  7         4848  
571             }
572             elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
573 9         16 ${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
  9         42  
  9         6181  
574             }
575             elsif ($keymethod eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
576 35         53 ${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $self->_getOAEPAlgorithm($self->{oaep_mgf_alg}), $self->{oaep_params});
  35         110  
  35         82  
577             } else {
578 0         0 die "Unsupported algorithm for key encyption $keymethod}";
579             }
580              
581 51 50       239 print "Encrypted key: ", encode_base64(${$key}) if $DEBUG;
  0         0  
582             }
583              
584             sub _setEncryptedData {
585 51     51   74 my $self = shift;
586 51         61 my $context = shift;
587 51         74 my $xpc = shift;
588 51         71 my $cipherdata = shift;
589              
590 51         163 my $node = $xpc->findnodes('xenc:EncryptedData/xenc:CipherData/xenc:CipherValue', $context);
591              
592 51         3328 $node->[0]->removeChildNodes();
593 51         251 $node->[0]->appendText($cipherdata);
594 51         180 return $context;
595             }
596              
597             sub _setKeyEncryptedData {
598 51     51   79 my $self = shift;
599 51         69 my $context = shift;
600 51         69 my $xpc = shift;
601 51         76 my $cipherdata = shift;
602              
603 51         67 my $node;
604              
605 51 50       122 if ($xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@Type', $context)
606             eq 'http://www.w3.org/2001/04/xmlenc#EncryptedKey')
607             {
608 0         0 my $id = $xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@URI', $context);
609 0         0 $id =~ s/#//g;
610              
611 0         0 my $keyinfo = $xpc->find('//*[@Id=\''. $id . '\']', $context);
612 0 0       0 if (! $keyinfo ) {
613 0         0 die "Unable to find EncryptedKey";
614             }
615              
616 0         0 $node = $keyinfo->[0]->findnodes('//xenc:EncryptedKey/xenc:CipherData', $context)->[0];
617             } else {
618 51         3456 $node = $xpc->findnodes('//dsig:KeyInfo/xenc:EncryptedKey/xenc:CipherData/xenc:CipherValue')->[0];
619             }
620 51         2234 $node->removeChildNodes();
621 51         329 $node->appendText($cipherdata);
622             }
623              
624             sub _remove_padding {
625 41     41   2428 my $self = shift;
626 41         63 my $padded = shift;
627              
628 41         80 my $len = length $padded;
629 41         94 my $padlen = ord substr $padded, $len - 1;
630 41         238 return substr $padded, 0, $len - $padlen;
631             }
632              
633             sub _add_padding {
634 30     30   62 my $self = shift;
635 30         47 my $data = shift;
636 30         57 my $blksize = shift;
637              
638 30         86 my $len = length $data;
639 30         72 my $padlen = $blksize - ($len % $blksize);
640 30         44 my @pad;
641 30         34 my $n = 0;
642 30         80 while ($n < $padlen -1 ) {
643 30         180 $pad[$n] = 176 + int(rand(80));
644 30         71 $n++;
645             }
646              
647 30         228 return $data . pack ("C*", @pad, $padlen);
648             }
649              
650             ##
651             ## _trim($string)
652             ##
653             ## Arguments:
654             ## $string: string String to remove whitespace
655             ##
656             ## Returns: string Trimmed String
657             ##
658             ## Trim the whitespace from the begining and end of the string
659             ##
660             sub _trim {
661 52     52   143 my $string = shift;
662 52         190 $string =~ s/^\s+//;
663 52         416 $string =~ s/\s+$//;
664 52         197 return $string;
665             }
666              
667             ##
668             ## _load_key($file)
669             ##
670             ## Arguments: $self->{ key }
671             ##
672             ## Returns: nothing
673             ##
674             ## Load the key and process it acording to its headers
675             ##
676             sub _load_key {
677 64     64   108 my $self = shift;
678 64         117 my $file = $self->{ key };
679              
680 64 50       3325 if ( open my $KEY, '<', $file ) {
681 64         283 my $text = '';
682 64         362 local $/ = undef;
683 64         1734 $text = <$KEY>;
684 64         879 close $KEY;
685 64 50       733 if ( $text =~ m/BEGIN ([DR]SA) PRIVATE KEY/ ) {
    50          
    50          
    0          
686 0         0 my $key_used = $1;
687              
688 0 0       0 if ( $key_used eq 'RSA' ) {
689 0         0 $self->_load_rsa_key( $text );
690             }
691             else {
692 0         0 $self->_load_dsa_key( $text );
693             }
694              
695 0         0 return 1;
696             } elsif ( $text =~ m/BEGIN EC PRIVATE KEY/ ) {
697 0         0 $self->_load_ecdsa_key( $text );
698             } elsif ( $text =~ m/BEGIN PRIVATE KEY/ ) {
699 64         208 $self->_load_rsa_key( $text );
700             } elsif ($text =~ m/BEGIN CERTIFICATE/) {
701 0         0 $self->_load_x509_key( $text );
702             }
703             else {
704 0         0 confess "Could not detect type of key $file.";
705             }
706             }
707             else {
708 0         0 confess "Could not load key $file: $!";
709             }
710              
711 64         284 return;
712             }
713              
714             ##
715             ## _load_rsa_key($key_text)
716             ##
717             ## Arguments:
718             ## $key_text: string RSA Private Key as String
719             ##
720             ## Returns: nothing
721             ##
722             ## Populate:
723             ## self->{KeyInfo}
724             ## self->{key_obj}
725             ## self->{key_type}
726             ##
727             sub _load_rsa_key {
728 64     64   125 my $self = shift;
729 64         159 my ($key_text) = @_;
730              
731 64         140 eval {
732 64         549 require Crypt::PK::RSA;
733             };
734 64 50       224 confess "Crypt::PK::RSA needs to be installed so that we can handle RSA keys." if $@;
735              
736 64         553 my $rsaKey = Crypt::PK::RSA->new(\$key_text );
737              
738 64 50       31383 if ( $rsaKey ) {
739 64         207 $self->{ key_obj } = $rsaKey;
740 64         175 $self->{ key_type } = 'rsa';
741              
742 64 50       200 if (!$self->{ x509 }) {
743 64         31170 my $keyhash = $rsaKey->key2hash();
744              
745 64         701 $self->{KeyInfo} = "
746            
747            
748             $keyhash->{N}
749             $keyhash->{d}
750            
751            
752             ";
753             }
754             }
755             else {
756 0         0 confess "did not get a new Crypt::PK::RSA object";
757             }
758             }
759              
760             ##
761             ## _load_x509_key($key_text)
762             ##
763             ## Arguments:
764             ## $key_text: string RSA Private Key as String
765             ##
766             ## Returns: nothing
767             ##
768             ## Populate:
769             ## self->{key_obj}
770             ## self->{key_type}
771             ##
772             sub _load_x509_key {
773 0     0   0 my $self = shift;
774 0         0 my $key_text = shift;
775              
776 0         0 eval {
777 0         0 require Crypt::OpenSSL::X509;
778             };
779 0 0       0 confess "Crypt::OpenSSL::X509 needs to be installed so that we
780             can handle X509 Certificates." if $@;
781              
782 0         0 my $x509Key = Crypt::OpenSSL::X509->new_private_key( $key_text );
783              
784 0 0       0 if ( $x509Key ) {
785 0         0 $x509Key->use_pkcs1_padding();
786 0         0 $self->{ key_obj } = $x509Key;
787 0         0 $self->{key_type} = 'x509';
788             }
789             else {
790 0         0 confess "did not get a new Crypt::OpenSSL::X509 object";
791             }
792             }
793              
794             ##
795             ## _load_cert_file()
796             ##
797             ## Arguments: none
798             ##
799             ## Returns: nothing
800             ##
801             ## Read the file name from $self->{ cert } and
802             ## Populate:
803             ## self->{key_obj}
804             ## $self->{KeyInfo}
805             ##
806             sub _load_cert_file {
807 52     52   108 my $self = shift;
808              
809 52         76 eval {
810 52         1742 require Crypt::OpenSSL::X509;
811             };
812              
813 52 50       94546 die "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certs.\n" if $@;
814              
815 52         91 my $file = $self->{ cert };
816 52 50       1039 if (!-r $file) {
817 0         0 die "Could not find certificate file $file";
818             }
819 52 50       1955 open my $CERT, '<', $file or die "Unable to open $file\n";
820 52         173 my $text = '';
821 52         287 local $/ = undef;
822 52         1187 $text = <$CERT>;
823 52         584 close $CERT;
824              
825 52         478 my $cert = Crypt::PK::RSA->new(\$text);
826 52 50       18512 die "Could not load certificate from $file" unless $cert;
827              
828 52         130 $self->{ cert_obj } = $cert;
829 52         177 my $cert_text = $cert->export_key_pem('public_x509');
830 52         8038 $cert_text =~ s/-----[^-]*-----//gm;
831 52         167 $self->{KeyInfo} = "\n"._trim($cert_text)."\n";
832 52         282 return;
833             }
834              
835             sub _create_encrypted_data_xml {
836 51     51   70 my $self = shift;
837              
838 51         87 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
839 51         407 my $doc = XML::LibXML::Document->new();
840              
841 51         88 my $xencns = 'http://www.w3.org/2001/04/xmlenc#';
842 51         82 my $dsigns = 'http://www.w3.org/2000/09/xmldsig#';
843 51         74 my $xenc11ns = 'http://www.w3.org/2009/xmlenc11#';
844              
845 51         245 my $encdata = $self->_create_node($doc, $xencns, $doc, 'xenc:EncryptedData',
846             {
847             Type => 'http://www.w3.org/2001/04/xmlenc#Element',
848             }
849             );
850              
851 51         1075 $doc->setDocumentElement ($encdata);
852              
853             my $encmethod = $self->_create_node(
854             $doc,
855             $xencns,
856             $encdata,
857             'xenc:EncryptionMethod',
858             {
859             Algorithm => $self->{data_enc_method},
860             }
861 51         1907 );
862              
863 51         595 my $keyinfo = $self->_create_node(
864             $doc,
865             $dsigns,
866             $encdata,
867             'dsig:KeyInfo',
868             );
869              
870 51         654 my $enckey = $self->_create_node(
871             $doc,
872             $xencns,
873             $keyinfo,
874             'xenc:EncryptedKey',
875             );
876              
877             my $kencmethod = $self->_create_node(
878             $doc,
879             $xencns,
880             $enckey,
881             'xenc:EncryptionMethod',
882             {
883             Algorithm => $self->{key_transport},
884             }
885 51         641 );
886              
887 51 100       641 if ($self->{'oaep_params'} ne '') {
888 1         3 my $oaep_params = $self->_create_node(
889             $doc,
890             $xencns,
891             $kencmethod,
892             'xenc:OAEPparams',
893             );
894             };
895              
896 51 100       160 if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
897             my $oaepmethod = $self->_create_node(
898             $doc,
899             $xenc11ns,
900             $kencmethod,
901             'xenc11:MGF',
902             {
903             Algorithm => $self->{oaep_mgf_alg},
904             }
905 35         100 );
906             };
907              
908 51         440 my $keyinfo2 = $self->_create_node(
909             $doc,
910             $dsigns,
911             $enckey,
912             'dsig:KeyInfo',
913             );
914              
915 51         588 my $keyname = $self->_create_node(
916             $doc,
917             $dsigns,
918             $keyinfo2,
919             'dsig:KeyName',
920             );
921              
922 51         589 my $keycipherdata = $self->_create_node(
923             $doc,
924             $xencns,
925             $enckey,
926             'xenc:CipherData',
927             );
928              
929 51         634 my $keyciphervalue = $self->_create_node(
930             $doc,
931             $xencns,
932             $keycipherdata,
933             'xenc:CipherValue',
934             );
935              
936 51         565 my $cipherdata = $self->_create_node(
937             $doc,
938             $xencns,
939             $encdata,
940             'xenc:CipherData',
941             );
942              
943 51         601 my $ciphervalue = $self->_create_node(
944             $doc,
945             $xencns,
946             $cipherdata,
947             'xenc:CipherValue',
948             );
949              
950 51         569 return $doc;
951             }
952              
953             sub _create_node {
954 597     597   1174 my $self = shift;
955 597         684 my $doc = shift;
956 597         792 my $nsuri = shift;
957 597         649 my $parent = shift;
958 597         665 my $name = shift;
959 597         664 my $attributes = shift;
960              
961 597         2985 my $node = $doc->createElementNS ($nsuri, $name);
962 597         1538 for (keys %$attributes) {
963             $node->addChild (
964             $doc->createAttribute (
965             #$node->setAttribute (
966 188         1939 $_ => $attributes->{$_}
967             )
968             );
969             }
970 597         2312 $parent->addChild($node);
971             }
972              
973             1;
974              
975             __END__