File Coverage

blib/lib/XML/Enc.pm
Criterion Covered Total %
statement 334 373 89.5
branch 64 102 62.7
condition n/a
subroutine 37 38 97.3
pod 3 3 100.0
total 438 516 84.8


line stmt bran cond sub pod time code
1 9     9   1124226 use strict;
  9         83  
  9         261  
2 9     9   45 use warnings;
  9         24  
  9         426  
3              
4             package XML::Enc;
5             our $VERSION = '0.11'; # VERSION
6              
7             # ABSTRACT: XML::Enc Encryption Support
8              
9 9     9   61 use Carp;
  9         15  
  9         531  
10 9     9   6407 use XML::LibXML;
  9         434262  
  9         61  
11 9     9   6749 use Crypt::PK::RSA;
  9         134473  
  9         450  
12 9     9   70 use Crypt::Mode::CBC;
  9         28  
  9         281  
13 9     9   3843 use Crypt::AuthEnc::GCM 0.062;
  9         3388  
  9         449  
14 9     9   4399 use MIME::Base64 qw/decode_base64 encode_base64/;
  9         5944  
  9         604  
15 9     9   67 use Crypt::PRNG qw( random_bytes );
  9         32  
  9         402  
16              
17 9     9   65 use vars qw($VERSION @EXPORT_OK %EXPORT_TAGS $DEBUG);
  9         28  
  9         38599  
18              
19             our $DEBUG = 0;
20              
21              
22             # Source: https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#sec-Alg-Block
23             # 5.2.1 Triple DES - 64 bit Initialization Vector (IV) (8 bytes)
24             # 5.2.2 AES - 128 bit initialization vector (IV) (16 bytes)
25              
26             my %encmethods = (
27             'http://www.w3.org/2001/04/xmlenc#tripledes-cbc' => {
28             ivsize => 8,
29             keysize => 24,
30             modename => 'DES_EDE' },
31             'http://www.w3.org/2001/04/xmlenc#aes128-cbc' => {
32             ivsize => '16',
33             keysize => 16,
34             modename => 'AES' },
35             'http://www.w3.org/2001/04/xmlenc#aes192-cbc' => {
36             ivsize => '16',
37             keysize => 24,
38             modename => 'AES' },
39             'http://www.w3.org/2001/04/xmlenc#aes256-cbc' => {
40             ivsize => '16',
41             keysize => 32,
42             modename => 'AES' },
43             'http://www.w3.org/2009/xmlenc11#aes128-gcm' => {
44             ivsize => '12',
45             keysize => 16,
46             modename => 'AES',
47             tagsize => 16 },
48             'http://www.w3.org/2009/xmlenc11#aes192-gcm' => {
49             ivsize => '12',
50             keysize => 24,
51             modename => 'AES',
52             tagsize => 16 },
53             'http://www.w3.org/2009/xmlenc11#aes256-gcm' => {
54             ivsize => '12',
55             keysize => 32,
56             modename => 'AES',
57             tagsize => 16 },
58             );
59              
60              
61             sub new {
62 64     64 1 49961 my $class = shift;
63 64         119 my $params = shift;
64 64         131 my $self = {};
65              
66 64         203 bless $self, $class;
67              
68 64 50       228 if ( exists $params->{ 'key' } ) {
69 64         162 $self->{key} = $params->{ 'key' };
70 64         166 $self->_load_key( $params->{ 'key' } );
71             }
72 64 100       208 if ( exists $params->{ 'cert' } ) {
73 52         116 $self->{cert} = $params->{ 'cert' };
74 52         150 $self->_load_cert_file( $params->{ 'cert' } );
75             }
76 64 50       196 if (exists $params->{'no_xml_declaration'}) {
77 64 50       201 $self->{'no_xml_declaration'} = $params->{'no_xml_declaration'} ? $params->{'no_xml_declaration'} : 0;
78             }
79              
80 64 100       153 my $enc_method = exists($params->{'data_enc_method'}) ? $params->{'data_enc_method'} : 'aes256-cbc';
81 64         177 $self->{'data_enc_method'} = $self->_setEncryptionMethod($enc_method);
82              
83 64 100       168 my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
84 64         156 $self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);
85              
86 64 100       191 my $oaep_mgf_alg = exists($params->{'oaep_mgf_alg'}) ? $params->{'oaep_mgf_alg'} : 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
87 64         156 $self->{'oaep_mgf_alg'} = $self->_setOAEPAlgorithm($oaep_mgf_alg);
88              
89 64 100       153 $self->{'oaep_params'} = exists($params->{'oaep_params'}) ? $params->{'oaep_params'} : '';
90              
91 64         306 return $self;
92             }
93              
94              
95             sub decrypt {
96 64     64 1 22273 my $self = shift;
97 64         164 my ($xml) = @_;
98              
99 64 50       184 die "You cannot decrypt XML without a private key." unless $self->{key};
100              
101 64         119 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
102              
103 64         242 my $doc = XML::LibXML->load_xml( string => $xml );
104              
105 63         18930 my $xpc = XML::LibXML::XPathContext->new($doc);
106 63         423 $xpc->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
107 63         210 $xpc->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
108 63         190 $xpc->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
109              
110 63         92 my $data;
111              
112 63         192 for my $encryptednode ($xpc->findnodes('//xenc:EncryptedData')) {
113 63         2609 my $type = $self->_getEncryptionType($xpc, $encryptednode);
114 63         4759 my $method = $self->_getEncryptionMethod($xpc, $encryptednode);
115 63         4229 my $keymethod = $self->_getKeyEncryptionMethod($xpc, $encryptednode);
116 63         261 my $encryptedkey = $self->_getKeyEncryptedData($xpc, $encryptednode);
117              
118             # Decrypt the key using specified method
119 63         5376 my $key = $self->_DecryptKey($keymethod, decode_base64($encryptedkey));
120              
121 62         464 my $encrypteddata = $self->_getEncryptedData($xpc, $encryptednode);
122              
123             # Decrypt the data using the decrypted key
124 62         7693 $data = $self->_DecryptData($method, $key, decode_base64($encrypteddata));
125              
126             # Load the decrypted XML text content and replace the EncryptedData
127             # in the original XML with the decrypted XML nodes
128 62 100       220 if ($type eq 'http://www.w3.org/2001/04/xmlenc#Element') {
129             # Check to see whether the decrypted data is really XML
130             # xmlsec has uses Element for encrypted Content
131 60         209 my $parser = XML::LibXML->new();
132 60         888 my $newnode = eval { $parser->load_xml(string => $data)->findnodes('//*')->[0] };
  60         204  
133              
134 60 50       22280 if (defined $newnode) {
135 60         500 $encryptednode->addSibling($newnode);
136 60         128 $encryptednode->unbindNode();
137             }
138             } else {
139             # http://www.w3.org/2001/04/xmlenc#Content
140 2         31 my $parent = $encryptednode->parentNode;
141 2         21 $parent->removeChildNodes;
142 2         15 $parent->appendText($data);
143             }
144             }
145              
146 62         1534 return $doc->serialize();
147             }
148              
149             sub encrypt {
150 51     51 1 239 my $self = shift;
151 51         104 my ($xml) = @_;
152              
153 51         96 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
154              
155             # Create the EncryptedData node
156 51         119 my ($encrypted) = $self->_create_encrypted_data_xml();
157              
158 51         4116 my $dom = XML::LibXML->load_xml( string => $xml);
159              
160 51         13131 my $xpc = XML::LibXML::XPathContext->new($encrypted);
161 51         328 $xpc->registerNs('dsig', 'http://www.w3.org/2000/09/xmldsig#');
162 51         157 $xpc->registerNs('xenc', 'http://www.w3.org/2001/04/xmlenc#');
163 51         160 $xpc->registerNs('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
164              
165             # Encrypt the data an empty key is passed by reference to allow
166             # the key to be generated at the same time the data is being encrypted
167 51         68 my $key;
168 51         99 my $method = $self->{data_enc_method};
169 51         140 my $encrypteddata = $self->_EncryptData ($method, $dom->serialize(), \$key);
170              
171             # Encrypt the Key immediately after the data is encrypted. It is passed by
172             # reference to reduce the number of times that the unencrypted key is
173             # stored in memory
174 51         199 $self->_EncryptKey($self->{key_transport}, \$key);
175              
176 51         250 my $base64_key = encode_base64($key);
177 51         138 my $base64_data = encode_base64($encrypteddata);
178              
179             # Insert OAEPparams into the XML
180 51 100       149 if ($self->{oaep_params} ne '') {
181 1         7 $encrypted = $self->_setOAEPparams($encrypted, $xpc, encode_base64($self->{oaep_params}));
182             }
183              
184             # Insert Encrypted data into XML
185 51         160 $encrypted = $self->_setEncryptedData($encrypted, $xpc, $base64_data);
186              
187             # Insert the Encrypted Key into the XML
188 51         747 $self->_setKeyEncryptedData($encrypted, $xpc, $base64_key);
189              
190 51         658 return $encrypted->serialize();
191             }
192              
193             sub _getEncryptionType {
194 63     63   103 my $self = shift;
195 63         83 my $xpc = shift;
196 63         90 my $context = shift;
197              
198 63         144 return $xpc->findvalue('@Type', $context)
199             }
200              
201             sub _getEncryptionMethod {
202 63     63   112 my $self = shift;
203 63         94 my $xpc = shift;
204 63         90 my $context = shift;
205              
206 63         139 return $xpc->findvalue('xenc:EncryptionMethod/@Algorithm', $context)
207             }
208              
209             sub _setEncryptionMethod {
210 64     64   124 my $self = shift;
211 64         83 my $method = shift;
212              
213 64         390 my %methods = (
214             'aes128-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
215             'aes192-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes192-cbc',
216             'aes256-cbc' => 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
217             'tripledes-cbc' => 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
218             'aes128-gcm' => 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
219             'aes192-gcm' => 'http://www.w3.org/2009/xmlenc11#aes192-gcm',
220             'aes256-gcm' => 'http://www.w3.org/2009/xmlenc11#aes256-gcm',
221             );
222              
223 64 50       326 return exists($methods{$method}) ? $methods{$method} : $methods{'aes256-cbc'};
224             }
225              
226             sub _setOAEPparams {
227 1     1   1 my $self = shift;
228 1         2 my $context = shift;
229 1         2 my $xpc = shift;
230 1         2 my $oaep_params = shift;
231              
232 1         5 my $node = $xpc->findnodes('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
233              
234 1         78 $node->[0]->removeChildNodes();
235 1         5 $node->[0]->appendText($oaep_params);
236 1         4 return $context;
237             }
238              
239             sub _setOAEPAlgorithm {
240 64     64   184 my $self = shift;
241 64         88 my $method = shift;
242              
243 64         272 my %methods = (
244             'mgf1sha1' => 'http://www.w3.org/2009/xmlenc11#mgf1sha1',
245             'mgf1sha224' => 'http://www.w3.org/2009/xmlenc11#mgf1sha224',
246             'mgf1sha256' => 'http://www.w3.org/2009/xmlenc11#mgf1sha256',
247             'mgf1sha384' => 'http://www.w3.org/2009/xmlenc11#mgf1sha384',
248             'mgf1sha512' => 'http://www.w3.org/2009/xmlenc11#mgf1sha512',
249             );
250              
251 64 100       221 return exists($methods{$method}) ? $methods{$method} : $methods{'mgf1sha1'};
252             }
253              
254             sub _getOAEPAlgorithm {
255 70     70   130 my $self = shift;
256 70         97 my $method = shift;
257              
258 70         319 my %methods = (
259             'http://www.w3.org/2009/xmlenc11#mgf1sha1' => 'SHA1',
260             'http://www.w3.org/2009/xmlenc11#mgf1sha224' => 'SHA224',
261             'http://www.w3.org/2009/xmlenc11#mgf1sha256' => 'SHA256',
262             'http://www.w3.org/2009/xmlenc11#mgf1sha384' => 'SHA384',
263             'http://www.w3.org/2009/xmlenc11#mgf1sha512' => 'SHA512',
264             );
265              
266 70 50       1016700 return exists($methods{$method}) ? $methods{$method} : $methods{'http://www.w3.org/2009/xmlenc11#mgf1sha1'};
267             }
268              
269             sub _getKeyEncryptionMethod {
270 63     63   110 my $self = shift;
271 63         89 my $xpc = shift;
272 63         93 my $context = shift;
273              
274 63         89 my %method;
275 63 100       151 if ($xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@Type', $context)
276             eq 'http://www.w3.org/2001/04/xmlenc#EncryptedKey')
277             {
278 1         63 my $id = $xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@URI', $context);
279 1         126 $id =~ s/#//g;
280              
281 1         20 my $keyinfo = $xpc->find('//*[@Id=\''. $id . '\']', $context);
282 1 50       97 if (! $keyinfo ) {
283 0         0 die "Unable to find EncryptedKey";
284             }
285 1         82 $method{Algorithm} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
286 1         82 $method{KeySize} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
287 1         53 $method{OAEPparams} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
288 1         50 $method{MGF} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:MGF/@Algorithm', $context);
289 1         57 return \%method;
290             }
291 62         3510 $method{Algorithm} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
292 62         3973 $method{KeySize} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
293 62         3380 $method{OAEPparams} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
294 62         3369 $method{MGF} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:MGF/@Algorithm', $context);
295 62         3678 return \%method;
296             }
297              
298             sub _setKeyEncryptionMethod {
299 64     64   89 my $self = shift;
300 64         99 my $method = shift;
301              
302 64         236 my %methods = (
303             'rsa-1_5' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',
304             'rsa-oaep-mgf1p' => 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
305             'rsa-oaep' => 'http://www.w3.org/2009/xmlenc11#rsa-oaep',
306             );
307              
308 64 100       222 return exists($methods{$method}) ? $methods{$method} : $methods{'rsa-oaep-mgf1p'};
309             }
310              
311             sub _DecryptData {
312 62     62   114 my $self = shift;
313 62         100 my $method = shift;
314 62         101 my $key = shift;
315 62         94 my $encrypteddata = shift;
316              
317 62         132 my $iv;
318             my $encrypted;
319 62         0 my $plaintext;
320              
321 62         282 my $ivsize = $encmethods{$method}->{ivsize};
322 62         129 my $tagsize = $encmethods{$method}->{tagsize};
323              
324 62         242 $iv = substr $encrypteddata, 0, $ivsize;
325 62         135 $encrypted = substr $encrypteddata, $ivsize;
326              
327             # XML Encryption 5.2 Block Encryption Algorithms
328             # The resulting cipher text is prefixed by the IV.
329 62 100       485 if (defined $encmethods{$method} & $method !~ /gcm/ ){
    50          
330 41         371 my $cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
331 41         190 $plaintext = $self->_remove_padding($cbc->decrypt($encrypted, $key, $iv));
332             } elsif (defined $encmethods{$method} & $method =~ /gcm/ ){
333 21         4513 my $gcm = Crypt::AuthEnc::GCM->new("AES", $key, $iv);
334              
335             # Note that GCM support for additional authentication
336             # data is not used in the XML specification.
337 21         72 my $tag = substr $encrypted, - $tagsize;
338 21         50 $encrypted = substr $encrypted, 0, (length $encrypted) - $tagsize;
339 21         126 $plaintext = $gcm->decrypt_add($encrypted);
340 21 50       165 if ( ! $gcm->decrypt_done($tag) ) {
341 0         0 die "Tag expected did not match returned Tag";
342             }
343             } else {
344 0         0 die "Unsupported Encryption Algorithm";
345             }
346              
347 62         178 return $plaintext;
348             }
349              
350             sub _EncryptData {
351 51     51   2679 my $self = shift;
352 51         90 my $method = shift;
353 51         72 my $data = shift;
354 51         72 my $key = shift;
355              
356 51         60 my $cipherdata;
357 51         191 my $ivsize = $encmethods{$method}->{ivsize};
358 51         90 my $keysize = $encmethods{$method}->{keysize};
359              
360 51         160 my $iv = random_bytes ( $ivsize);
361 51         880 ${$key} = random_bytes ( $keysize);
  51         384  
362              
363 51 100       328 if (defined $encmethods{$method} & $method !~ /gcm/ ){
    50          
364 30         341 my $cbc = Crypt::Mode::CBC->new($encmethods{$method}->{modename}, 0);
365             # XML Encryption 5.2 Block Encryption Algorithms
366             # The resulting cipher text is prefixed by the IV.
367 30         105 $data = $self->_add_padding($data, $ivsize);
368 30         68 $cipherdata = $iv . $cbc->encrypt($data, ${$key}, $iv);
  30         134  
369             } elsif (defined $encmethods{$method} & $method =~ /gcm/ ){
370 21         57 my $gcm = Crypt::AuthEnc::GCM->new($encmethods{$method}->{modename}, ${$key}, $iv);
  21         4487  
371              
372             # Note that GCM support for additional authentication
373             # data is not used in the XML specification.
374 21         185 my $encrypted = $gcm->encrypt_add($data);
375 21         91 my $tag = $gcm->encrypt_done();
376              
377 21         125 $cipherdata = $iv . $encrypted . $tag;
378             } else {
379 0         0 die "Unsupported Encryption Algorithm";
380             }
381              
382 51         1251 return $cipherdata;
383             }
384              
385             sub _DecryptKey {
386 63     63   116 my $self = shift;
387 63         95 my $keymethod = shift;
388 63         94 my $encryptedkey = shift;
389              
390 63 100       284 if ($keymethod->{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
    100          
    50          
391 12         338999 return $self->{key_obj}->decrypt($encryptedkey, 'v1.5');
392             }
393             elsif ($keymethod->{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
394 16         453337 return $self->{key_obj}->decrypt($encryptedkey, 'oaep', 'SHA1', decode_base64($keymethod->{OAEPparams}));
395             }
396             elsif ($keymethod->{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
397 35         92 return $self->{key_obj}->decrypt($encryptedkey, 'oaep', $self->_getOAEPAlgorithm($keymethod->{MGF}), decode_base64($keymethod->{OAEPparams}));
398             } else {
399 0         0 die "Unsupported Key Encryption Method";
400             }
401              
402 0 0       0 print "Decrypted key: ", encode_base64($self->{key_obj}->decrypt($encryptedkey)) if $DEBUG;
403 0         0 return $self->{key_obj}->decrypt($encryptedkey);
404             }
405              
406             sub _EncryptKey {
407 51     51   70 my $self = shift;
408 51         66 my $keymethod = shift;
409 51         70 my $key = shift;
410              
411 51         82 my $rsa_pub = $self->{cert_obj};
412              
413 51 100       167 if ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
    100          
    50          
414 7         11 ${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
  7         21  
  7         4597  
415             }
416             elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
417 9         19 ${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1', $self->{oaep_params});
  9         38  
  9         6102  
418             }
419             elsif ($keymethod eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
420 35         51 ${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $self->_getOAEPAlgorithm($self->{oaep_mgf_alg}), $self->{oaep_params});
  35         113  
  35         92  
421             } else {
422 0         0 die "Unsupported Key Encryption Method";
423             }
424              
425 51 50       182 print "Encrypted key: ", encode_base64(${$key}) if $DEBUG;
  0         0  
426             }
427              
428             sub _getEncryptedData {
429 62     62   144 my $self = shift;
430 62         84 my $xpc = shift;
431 62         102 my $context = shift;
432              
433 62         320 return $xpc->findvalue('xenc:CipherData/xenc:CipherValue', $context);
434             }
435              
436             sub _setEncryptedData {
437 51     51   79 my $self = shift;
438 51         67 my $context = shift;
439 51         64 my $xpc = shift;
440 51         78 my $cipherdata = shift;
441              
442 51         164 my $node = $xpc->findnodes('xenc:EncryptedData/xenc:CipherData/xenc:CipherValue', $context);
443              
444 51         3236 $node->[0]->removeChildNodes();
445 51         246 $node->[0]->appendText($cipherdata);
446 51         176 return $context;
447             }
448              
449             sub _getKeyEncryptedData {
450 63     63   118 my $self = shift;
451 63         89 my $xpc = shift;
452 63         128 my $context = shift;
453              
454 63 100       164 if ($xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@Type', $context)
455             eq 'http://www.w3.org/2001/04/xmlenc#EncryptedKey')
456             {
457 1         69 my $id = $xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@URI', $context);
458 1         65 $id =~ s/#//g;
459              
460 1         7 my $keyinfo = $xpc->find('//*[@Id=\''. $id . '\']', $context);
461 1 50       73 if (! $keyinfo ) {
462 0         0 die "Unable to find EncryptedKey";
463             }
464              
465 1         29 return $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:CipherData/xenc:CipherValue', $context);
466             }
467              
468 62         3300 return $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:CipherData/xenc:CipherValue', $context);
469             }
470              
471             sub _setKeyEncryptedData {
472 51     51   74 my $self = shift;
473 51         81 my $context = shift;
474 51         65 my $xpc = shift;
475 51         74 my $cipherdata = shift;
476              
477 51         64 my $node;
478              
479 51 50       130 if ($xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@Type', $context)
480             eq 'http://www.w3.org/2001/04/xmlenc#EncryptedKey')
481             {
482 0         0 my $id = $xpc->findvalue('dsig:KeyInfo/dsig:RetrievalMethod/@URI', $context);
483 0         0 $id =~ s/#//g;
484              
485 0         0 my $keyinfo = $xpc->find('//*[@Id=\''. $id . '\']', $context);
486 0 0       0 if (! $keyinfo ) {
487 0         0 die "Unable to find EncryptedKey";
488             }
489              
490 0         0 $node = $keyinfo->[0]->findnodes('//xenc:EncryptedKey/xenc:CipherData', $context)->[0];
491             } else {
492 51         3322 $node = $xpc->findnodes('//dsig:KeyInfo/xenc:EncryptedKey/xenc:CipherData/xenc:CipherValue')->[0];
493             }
494 51         2112 $node->removeChildNodes();
495 51         323 $node->appendText($cipherdata);
496             }
497              
498             sub _remove_padding {
499 41     41   2228 my $self = shift;
500 41         60 my $padded = shift;
501              
502 41         71 my $len = length $padded;
503 41         101 my $padlen = ord substr $padded, $len - 1;
504 41         192 return substr $padded, 0, $len - $padlen;
505             }
506              
507             sub _add_padding {
508 30     30   73 my $self = shift;
509 30         77 my $data = shift;
510 30         42 my $blksize = shift;
511              
512 30         73 my $len = length $data;
513 30         68 my $padlen = $blksize - ($len % $blksize);
514 30         48 my @pad;
515 30         47 my $n = 0;
516 30         90 while ($n < $padlen -1 ) {
517 30         178 $pad[$n] = 176 + int(rand(80));
518 30         79 $n++;
519             }
520              
521 30         217 return $data . pack ("C*", @pad, $padlen);
522             }
523              
524             ##
525             ## _trim($string)
526             ##
527             ## Arguments:
528             ## $string: string String to remove whitespace
529             ##
530             ## Returns: string Trimmed String
531             ##
532             ## Trim the whitespace from the begining and end of the string
533             ##
534             sub _trim {
535 52     52   110 my $string = shift;
536 52         193 $string =~ s/^\s+//;
537 52         451 $string =~ s/\s+$//;
538 52         292 return $string;
539             }
540              
541             ##
542             ## _load_key($file)
543             ##
544             ## Arguments: $self->{ key }
545             ##
546             ## Returns: nothing
547             ##
548             ## Load the key and process it acording to its headers
549             ##
550             sub _load_key {
551 64     64   110 my $self = shift;
552 64         111 my $file = $self->{ key };
553              
554 64 50       3109 if ( open my $KEY, '<', $file ) {
555 64         218 my $text = '';
556 64         349 local $/ = undef;
557 64         1680 $text = <$KEY>;
558 64         799 close $KEY;
559 64 50       825 if ( $text =~ m/BEGIN ([DR]SA) PRIVATE KEY/ ) {
    50          
    50          
    0          
560 0         0 my $key_used = $1;
561              
562 0 0       0 if ( $key_used eq 'RSA' ) {
563 0         0 $self->_load_rsa_key( $text );
564             }
565             else {
566 0         0 $self->_load_dsa_key( $text );
567             }
568              
569 0         0 return 1;
570             } elsif ( $text =~ m/BEGIN EC PRIVATE KEY/ ) {
571 0         0 $self->_load_ecdsa_key( $text );
572             } elsif ( $text =~ m/BEGIN PRIVATE KEY/ ) {
573 64         212 $self->_load_rsa_key( $text );
574             } elsif ($text =~ m/BEGIN CERTIFICATE/) {
575 0         0 $self->_load_x509_key( $text );
576             }
577             else {
578 0         0 confess "Could not detect type of key $file.";
579             }
580             }
581             else {
582 0         0 confess "Could not load key $file: $!";
583             }
584              
585 64         280 return;
586             }
587              
588             ##
589             ## _load_rsa_key($key_text)
590             ##
591             ## Arguments:
592             ## $key_text: string RSA Private Key as String
593             ##
594             ## Returns: nothing
595             ##
596             ## Populate:
597             ## self->{KeyInfo}
598             ## self->{key_obj}
599             ## self->{key_type}
600             ##
601             sub _load_rsa_key {
602 64     64   150 my $self = shift;
603 64         159 my ($key_text) = @_;
604              
605 64         119 eval {
606 64         447 require Crypt::PK::RSA;
607             };
608 64 50       177 confess "Crypt::PK::RSA needs to be installed so that we can handle RSA keys." if $@;
609              
610 64         498 my $rsaKey = Crypt::PK::RSA->new(\$key_text );
611              
612 64 50       30828 if ( $rsaKey ) {
613 64         171 $self->{ key_obj } = $rsaKey;
614 64         134 $self->{ key_type } = 'rsa';
615              
616 64 50       185 if (!$self->{ x509 }) {
617 64         27715 my $keyhash = $rsaKey->key2hash();
618              
619 64         653 $self->{KeyInfo} = "
620            
621            
622             $keyhash->{N}
623             $keyhash->{d}
624            
625            
626             ";
627             }
628             }
629             else {
630 0         0 confess "did not get a new Crypt::PK::RSA object";
631             }
632             }
633              
634             ##
635             ## _load_x509_key($key_text)
636             ##
637             ## Arguments:
638             ## $key_text: string RSA Private Key as String
639             ##
640             ## Returns: nothing
641             ##
642             ## Populate:
643             ## self->{key_obj}
644             ## self->{key_type}
645             ##
646             sub _load_x509_key {
647 0     0   0 my $self = shift;
648 0         0 my $key_text = shift;
649              
650 0         0 eval {
651 0         0 require Crypt::OpenSSL::X509;
652             };
653 0 0       0 confess "Crypt::OpenSSL::X509 needs to be installed so that we
654             can handle X509 Certificates." if $@;
655              
656 0         0 my $x509Key = Crypt::OpenSSL::X509->new_private_key( $key_text );
657              
658 0 0       0 if ( $x509Key ) {
659 0         0 $x509Key->use_pkcs1_padding();
660 0         0 $self->{ key_obj } = $x509Key;
661 0         0 $self->{key_type} = 'x509';
662             }
663             else {
664 0         0 confess "did not get a new Crypt::OpenSSL::X509 object";
665             }
666             }
667              
668             ##
669             ## _load_cert_file()
670             ##
671             ## Arguments: none
672             ##
673             ## Returns: nothing
674             ##
675             ## Read the file name from $self->{ cert } and
676             ## Populate:
677             ## self->{key_obj}
678             ## $self->{KeyInfo}
679             ##
680             sub _load_cert_file {
681 52     52   102 my $self = shift;
682              
683 52         90 eval {
684 52         1839 require Crypt::OpenSSL::X509;
685             };
686              
687 52 50       92450 confess "Crypt::OpenSSL::X509 needs to be installed so that we can handle X509 certs." if $@;
688              
689 52         131 my $file = $self->{ cert };
690 52 50       2167 if ( open my $CERT, '<', $file ) {
691 52         181 my $text = '';
692 52         299 local $/ = undef;
693 52         1185 $text = <$CERT>;
694 52         573 close $CERT;
695              
696 52         467 my $cert = Crypt::PK::RSA->new(\$text);
697 52 50       18616 if ( $cert ) {
698 52         123 $self->{ cert_obj } = $cert;
699 52         197 my $cert_text = $cert->export_key_pem('public_x509');
700 52         7425 $cert_text =~ s/-----[^-]*-----//gm;
701 52         205 $self->{KeyInfo} = "\n"._trim($cert_text)."\n";
702             }
703             else {
704 0         0 confess "Could not load certificate from $file";
705             }
706             }
707             else {
708 0         0 confess "Could not find certificate file $file";
709             }
710              
711 52         222 return;
712             }
713              
714             sub _create_encrypted_data_xml {
715 51     51   74 my $self = shift;
716              
717 51         98 local $XML::LibXML::skipXMLDeclaration = $self->{ no_xml_declaration };
718 51         454 my $doc = XML::LibXML::Document->new();
719              
720 51         98 my $xencns = 'http://www.w3.org/2001/04/xmlenc#';
721 51         74 my $dsigns = 'http://www.w3.org/2000/09/xmldsig#';
722              
723 51         203 my $encdata = $self->_create_node($doc, $xencns, $doc, 'xenc:EncryptedData',
724             {
725             Type => 'http://www.w3.org/2001/04/xmlenc#Element',
726             }
727             );
728              
729 51         996 $doc->setDocumentElement ($encdata);
730              
731             my $encmethod = $self->_create_node(
732             $doc,
733             $xencns,
734             $encdata,
735             'xenc:EncryptionMethod',
736             {
737             Algorithm => $self->{data_enc_method},
738             }
739 51         1784 );
740              
741 51         659 my $keyinfo = $self->_create_node(
742             $doc,
743             $dsigns,
744             $encdata,
745             'dsig:KeyInfo',
746             );
747              
748 51         561 my $enckey = $self->_create_node(
749             $doc,
750             $xencns,
751             $keyinfo,
752             'xenc:EncryptedKey',
753             );
754              
755             my $kencmethod = $self->_create_node(
756             $doc,
757             $xencns,
758             $enckey,
759             'xenc:EncryptionMethod',
760             {
761             Algorithm => $self->{key_transport},
762             }
763 51         597 );
764              
765 51 100       642 if ($self->{'oaep_params'} ne '') {
766 1         3 my $oaep_params = $self->_create_node(
767             $doc,
768             $xencns,
769             $kencmethod,
770             'xenc:OAEPparams',
771             );
772             };
773              
774 51 100       162 if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
775             my $oaepmethod = $self->_create_node(
776             $doc,
777             $xencns,
778             $kencmethod,
779             'xenc:MGF',
780             {
781             Algorithm => $self->{oaep_mgf_alg},
782             }
783 35         109 );
784             };
785              
786 51         423 my $keyinfo2 = $self->_create_node(
787             $doc,
788             $dsigns,
789             $enckey,
790             'dsig:KeyInfo',
791             );
792              
793 51         596 my $keyname = $self->_create_node(
794             $doc,
795             $dsigns,
796             $keyinfo2,
797             'dsig:KeyName',
798             );
799              
800 51         591 my $keycipherdata = $self->_create_node(
801             $doc,
802             $xencns,
803             $enckey,
804             'xenc:CipherData',
805             );
806              
807 51         618 my $keyciphervalue = $self->_create_node(
808             $doc,
809             $xencns,
810             $keycipherdata,
811             'xenc:CipherValue',
812             );
813              
814 51         540 my $cipherdata = $self->_create_node(
815             $doc,
816             $xencns,
817             $encdata,
818             'xenc:CipherData',
819             );
820              
821 51         541 my $ciphervalue = $self->_create_node(
822             $doc,
823             $xencns,
824             $cipherdata,
825             'xenc:CipherValue',
826             );
827              
828 51         527 return $doc;
829             }
830              
831             sub _create_node {
832 597     597   1072 my $self = shift;
833 597         648 my $doc = shift;
834 597         716 my $nsuri = shift;
835 597         684 my $parent = shift;
836 597         720 my $name = shift;
837 597         656 my $attributes = shift;
838              
839 597         2857 my $node = $doc->createElementNS ($nsuri, $name);
840 597         1540 for (keys %$attributes) {
841             $node->addChild (
842             $doc->createAttribute (
843             #$node->setAttribute (
844 188         1933 $_ => $attributes->{$_}
845             )
846             );
847             }
848 597         2311 $parent->addChild($node);
849             }
850              
851             1;
852              
853             __END__