Skip to content

Commit a64c613

Browse files
committed
Move to Crypt::PK::RSA and support OAEP with MGF element
1 parent 0c9a35a commit a64c613

File tree

3 files changed

+94
-23
lines changed

3 files changed

+94
-23
lines changed

lib/XML/Enc.pm

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package XML::Enc;
88

99
use Carp;
1010
use XML::LibXML;
11-
use Crypt::OpenSSL::RSA;
11+
use Crypt::PK::RSA;
1212
use Crypt::Mode::CBC;
1313
use Crypt::AuthEnc::GCM 0.062;
1414
use MIME::Base64 qw/decode_base64 encode_base64/;
@@ -143,6 +143,8 @@ Used in encryption. Optional. Default method: rsa-1_5
143143
144144
=item * L<rsa-oaep-mgf1p|https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#rsa-oaep-mgf1p>
145145
146+
=item * L<rsa-oaep (Experimental)| http://www.w3.org/2009/xmlenc11#rsa-oaep>
147+
146148
=back
147149
148150
=back
@@ -174,6 +176,9 @@ sub new {
174176
my $key_method = exists($params->{'key_transport'}) ? $params->{'key_transport'} : 'rsa-oaep-mgf1p ';
175177
$self->{'key_transport'} = $self->_setKeyEncryptionMethod($key_method);
176178

179+
my $oaep_method = exists($params->{'oaep_method'}) ? $params->{'oaep_method'} : 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
180+
$self->{'oaep_method'} = $self->_setOAEPAlgorithm($oaep_method);
181+
177182
return $self;
178183
}
179184

@@ -334,6 +339,36 @@ sub _setEncryptionMethod {
334339
return exists($methods{$method}) ? $methods{$method} : $methods{'aes256-cbc'};
335340
}
336341

342+
sub _setOAEPAlgorithm {
343+
my $self = shift;
344+
my $method = shift;
345+
346+
my %methods = (
347+
'mgf1sha1' => 'http://www.w3.org/2009/xmlenc11#mgf1sha1',
348+
'mgf1sha224' => 'http://www.w3.org/2009/xmlenc11#mgf1sha224',
349+
'mgf1sha256' => 'http://www.w3.org/2009/xmlenc11#mgf1sha256',
350+
'mgf1sha384' => 'http://www.w3.org/2009/xmlenc11#mgf1sha384',
351+
'mgf1sha512' => 'http://www.w3.org/2009/xmlenc11#mgf1sha512',
352+
);
353+
354+
return exists($methods{$method}) ? $methods{$method} : $methods{'mgf1sha1'};
355+
}
356+
357+
sub _getOAEPAlgorithm {
358+
my $self = shift;
359+
my $method = shift;
360+
361+
my %methods = (
362+
'http://www.w3.org/2009/xmlenc11#mgf1sha1' => 'SHA1',
363+
'http://www.w3.org/2009/xmlenc11#mgf1sha224' => 'SHA224',
364+
'http://www.w3.org/2009/xmlenc11#mgf1sha256' => 'SHA256',
365+
'http://www.w3.org/2009/xmlenc11#mgf1sha384' => 'SHA384',
366+
'http://www.w3.org/2009/xmlenc11#mgf1sha512' => 'SHA512',
367+
);
368+
369+
return exists($methods{$method}) ? $methods{$method} : $methods{'http://www.w3.org/2009/xmlenc11#mgf1sha1'};
370+
}
371+
337372
sub _getKeyEncryptionMethod {
338373
my $self = shift;
339374
my $xpc = shift;
@@ -353,11 +388,13 @@ sub _getKeyEncryptionMethod {
353388
$method{Algorithm} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
354389
$method{KeySize} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
355390
$method{OAEPparams} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
391+
$method{MGF} = $keyinfo->[0]->findvalue('//xenc:EncryptedKey/xenc:EncryptionMethod/xenc:MGF/@Algorithm', $context);
356392
return \%method;
357393
}
358394
$method{Algorithm} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/@Algorithm', $context);
359395
$method{KeySize} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:KeySize', $context);
360396
$method{OAEPparams} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:OAEPparams', $context);
397+
$method{MGF} = $xpc->findvalue('dsig:KeyInfo/xenc:EncryptedKey/xenc:EncryptionMethod/xenc:MGF/@Algorithm', $context);
361398
return \%method;
362399
}
363400

@@ -368,6 +405,7 @@ sub _setKeyEncryptionMethod {
368405
my %methods = (
369406
'rsa-1_5' => 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',
370407
'rsa-oaep-mgf1p' => 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
408+
'rsa-oaep' => 'http://www.w3.org/2009/xmlenc11#rsa-oaep',
371409
);
372410

373411
return exists($methods{$method}) ? $methods{$method} : $methods{'rsa-oaep-mgf1p'};
@@ -453,10 +491,13 @@ sub _DecryptKey {
453491
my $encryptedkey = shift;
454492

455493
if ($keymethod->{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
456-
$self->{key_obj}->use_pkcs1_padding;
494+
return $self->{key_obj}->decrypt($encryptedkey, 'v1.5');
457495
}
458496
elsif ($keymethod->{Algorithm} eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
459-
$self->{key_obj}->use_pkcs1_oaep_padding;
497+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', 'SHA1', decode_base64($keymethod->{OAEPparams}));
498+
}
499+
elsif ($keymethod->{Algorithm} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
500+
return $self->{key_obj}->decrypt($encryptedkey, 'oaep', $self->_getOAEPAlgorithm($keymethod->{MGF}), decode_base64($keymethod->{OAEPparams}));
460501
} else {
461502
die "Unsupported Key Encryption Method";
462503
}
@@ -470,18 +511,21 @@ sub _EncryptKey {
470511
my $keymethod = shift;
471512
my $key = shift;
472513

473-
my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($self->{cert_obj}->pubkey);
514+
my $rsa_pub = $self->{cert_obj};
515+
474516
if ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-1_5') {
475-
$rsa_pub->use_pkcs1_padding;
517+
${$key} = $rsa_pub->encrypt(${$key}, 'v1.5');
476518
}
477519
elsif ($keymethod eq 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p') {
478-
$rsa_pub->use_pkcs1_oaep_padding;
520+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', 'SHA1');
521+
}
522+
elsif ($keymethod eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
523+
${$key} = $rsa_pub->encrypt(${$key}, 'oaep', $self->_getOAEPAlgorithm($self->{oaep_method}));
479524
} else {
480525
die "Unsupported Key Encryption Method";
481526
}
482527

483528
print "Encrypted key: ", encode_base64(${$key}) if $DEBUG;
484-
${$key} = $rsa_pub->encrypt(${$key});
485529
}
486530

487531
sub _getEncryptedData {
@@ -662,37 +706,31 @@ sub _load_rsa_key {
662706
my ($key_text) = @_;
663707

664708
eval {
665-
require Crypt::OpenSSL::RSA;
709+
require Crypt::PK::RSA;
666710
};
667-
confess "Crypt::OpenSSL::RSA needs to be installed so that we can handle RSA keys." if $@;
711+
confess "Crypt::PK::RSA needs to be installed so that we can handle RSA keys." if $@;
668712

669-
my $rsaKey = Crypt::OpenSSL::RSA->new_private_key( $key_text );
713+
my $rsaKey = Crypt::PK::RSA->new(\$key_text );
670714

671715
if ( $rsaKey ) {
672-
$rsaKey->use_pkcs1_padding();
673716
$self->{ key_obj } = $rsaKey;
674717
$self->{ key_type } = 'rsa';
675718

676719
if (!$self->{ x509 }) {
677-
my $bigNum = ( $rsaKey->get_key_parameters() )[1];
678-
my $bin = $bigNum->to_bin();
679-
my $exp = encode_base64( $bin, '' );
720+
my $keyhash = $rsaKey->key2hash();
680721

681-
$bigNum = ( $rsaKey->get_key_parameters() )[0];
682-
$bin = $bigNum->to_bin();
683-
my $mod = encode_base64( $bin, '' );
684722
$self->{KeyInfo} = "<dsig:KeyInfo>
685723
<dsig:KeyValue>
686724
<dsig:RSAKeyValue>
687-
<dsig:Modulus>$mod</dsig:Modulus>
688-
<dsig:Exponent>$exp</dsig:Exponent>
725+
<dsig:Modulus>$keyhash->{N}</dsig:Modulus>
726+
<dsig:Exponent>$keyhash->{d}</dsig:Exponent>
689727
</dsig:RSAKeyValue>
690728
</dsig:KeyValue>
691729
</dsig:KeyInfo>";
692730
}
693731
}
694732
else {
695-
confess "did not get a new Crypt::OpenSSL::RSA object";
733+
confess "did not get a new Crypt::PK::RSA object";
696734
}
697735
}
698736

@@ -758,10 +796,10 @@ sub _load_cert_file {
758796
$text = <$CERT>;
759797
close $CERT;
760798

761-
my $cert = Crypt::OpenSSL::X509->new_from_string($text);
799+
my $cert = Crypt::PK::RSA->new(\$text);
762800
if ( $cert ) {
763801
$self->{ cert_obj } = $cert;
764-
my $cert_text = $cert->as_string;
802+
my $cert_text = $cert->export_key_pem('public_x509');
765803
$cert_text =~ s/-----[^-]*-----//gm;
766804
$self->{KeyInfo} = "<dsig:KeyInfo><dsig:X509Data><dsig:X509Certificate>\n"._trim($cert_text)."\n</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo>";
767805
}
@@ -827,6 +865,18 @@ sub _create_encrypted_data_xml {
827865
}
828866
);
829867

868+
if ($self->{key_transport} eq 'http://www.w3.org/2009/xmlenc11#rsa-oaep') {
869+
my $oaepmethod = $self->_create_node(
870+
$doc,
871+
$xencns,
872+
$kencmethod,
873+
'xenc:MGF',
874+
{
875+
Algorithm => $self->{oaep_method},
876+
}
877+
);
878+
};
879+
830880
my $keyinfo2 = $self->_create_node(
831881
$doc,
832882
$dsigns,

t/06-test-encryption-methods.t

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use strict;
22
use warnings;
3-
use Test::More tests => 56;
3+
use Test::More tests => 126;
44
use XML::Enc;
55
use MIME::Base64 qw/decode_base64 encode_base64/;
66
use File::Which;
@@ -14,6 +14,7 @@ XML
1414

1515
my @key_methods = qw/rsa-1_5 rsa-oaep-mgf1p/;
1616
my @data_methods = qw/aes128-cbc aes192-cbc aes256-cbc tripledes-cbc aes128-gcm aes192-gcm aes256-gcm/;
17+
my @oaep_methods = qw/mgf1sha1 mgf1sha224 mgf1sha256 mgf1sha384 mgf1sha512/;
1718

1819
foreach my $km (@key_methods) {
1920
foreach my $dm (@data_methods) {
@@ -47,7 +48,26 @@ foreach my $km (@key_methods) {
4748
or warn "calling xmlsec1 failed: '$verify_response'\n";
4849
unlink 'tmp.xml';
4950
}
51+
}
52+
}
5053

54+
foreach my $om (@oaep_methods) {
55+
foreach my $dm (@data_methods) {
56+
my $encrypter = XML::Enc->new(
57+
{
58+
key => 't/sign-private.pem',
59+
cert => 't/sign-certonly.pem',
60+
data_enc_method => $dm,
61+
key_transport => 'rsa-oaep',
62+
oaep_method => $om,
63+
no_xml_declaration => 1
64+
}
65+
);
66+
67+
my $encrypted = $encrypter->encrypt($xml);
68+
ok($encrypted =~ /EncryptedData/, "Successfully Encrypted: Key Method 'rsa-oaep' with $om Data Method $dm");
69+
70+
ok($encrypter->decrypt($encrypted) =~ /XML-SIG_1/, "Successfully Decrypted with XML::Enc");
5171
}
5272
}
5373
done_testing;

t/07-decrypt-xmlsec.t

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ CONTENT
104104

105105
SKIP: {
106106
skip "xmlsec1 not installed", 5 unless which('xmlsec1');
107+
skip "xmlsec1 no support for MGF element", 5 if $km eq 'rsa-oaep';
107108
my $version;
108109
if (`xmlsec1 version` =~ m/(\d+\.\d+\.\d+)/) {
109110
$version = $1;

0 commit comments

Comments
 (0)