package Crypt::Perl::RSA::PrivateKey;
=encoding utf-8
=head1 NAME
Crypt::Perl::RSA::PrivateKey - object representation of an RSA private key
=head1 SYNOPSIS
#You’ll probably instantiate this class using Parser.pm
#or Generate.pm.
#cf. JSON Web Algorithms (RFC 7518, page 5)
#These return an octet string.
$sig = $prkey->sign_RS256($message);
$sig = $prkey->sign_RS384($message);
$sig = $prkey->sign_RS512($message);
#These return 1 or 0 to indicate verification or non-verification.
$prkey->verify_RS256($message, $sig);
$prkey->verify_RS384($message, $sig);
$prkey->verify_RS512($message, $sig);
#----------------------------------------------------------------------
my $enc = $prkey->encrypt_raw($payload);
my $orig = $prkey->decrypt_raw($enc);
#----------------------------------------------------------------------
my $der = $prkey->to_der();
my $pem = $prkey->to_pem();
#For use in creating PKCS #10 CSRs and X.509 certificates
my $pub_der = $prkey->to_subject_public_der();
my $pbkey = $prkey->get_public_key();
#----------------------------------------------------------------------
$prkey->version(); #scalar, integer
$prkey->size(); #modulus length, in bits
$prkey->modulus_byte_length();
#----------------------------------------------------------------------
# The following all return instances of Crypt::Perl::BigInt,
# a subclass of Math::BigInt.
# The pairs (e.g., modulus() and N()) are aliases.
#----------------------------------------------------------------------
$prkey->modulus();
$prkey->N();
$prkey->publicExponent();
$prkey->E();
$prkey->privateExponent();
$prkey->D();
$prkey->prime1();
$prkey->P();
$prkey->prime2();
$prkey->Q();
$prkey->exponent1();
$prkey->DP();
$prkey->exponent2();
$prkey->DQ();
$prkey->coefficient();
$prkey->QINV();
=cut
use strict;
use warnings;
use parent qw(
Crypt::Perl::RSA::KeyBase
);
use Module::Load ();
use Crypt::Perl::RNG ();
use Crypt::Perl::X ();
use constant _PEM_HEADER => 'RSA PRIVATE KEY';
use constant _ASN1_MACRO => 'RSAPrivateKey';
BEGIN {
__PACKAGE__->mk_ro_accessors(
qw(
version
publicExponent
privateExponent
prime1
prime2
exponent1
exponent2
coefficient
)
);
*E = \&publicExponent;
*D = \&privateExponent;
*P = \&prime1;
*Q = \&prime2;
*DP = \&exponent1;
*DQ = \&exponent2;
*QINV = \&coefficient;
*to_subject_public_der = __PACKAGE__->can('_to_subject_public_der');
}
sub sign_RS256 {
my ($self, $msg) = @_;
return $self->_sign($msg, 'Digest::SHA', 'sha256', 'PKCS1_v1_5');
}
sub sign_RS384 {
my ($self, $msg) = @_;
return $self->_sign($msg, 'Digest::SHA', 'sha384', 'PKCS1_v1_5');
}
sub sign_RS512 {
my ($self, $msg) = @_;
return $self->_sign($msg, 'Digest::SHA', 'sha512', 'PKCS1_v1_5');
}
sub get_public_key {
my ($self) = @_;
require Crypt::Perl::RSA::PublicKey;
return Crypt::Perl::RSA::PublicKey->new( {
modulus => $self->{'modulus'},
publicExponent => $self->{'publicExponent'},
} );
}
sub get_struct_for_private_jwk {
my ($self) = @_;
require MIME::Base64;
my $jwk = $self->get_struct_for_public_jwk();
my %augment = qw(
d D
p P
q Q
dp DP
dq DQ
qi QINV
);
for my $k (keys %augment) {
my $accessor = $augment{$k};
$jwk->{$k} = MIME::Base64::encode_base64url( $self->$accessor()->as_bytes() );
}
return $jwk;
}
#----------------------------------------------------------------------
#This function, in tandem with encrypt_raw(), represents the fundamental
#mathematical truth on which RSA rests.
#
sub decrypt_raw {
my ($self, $x) = @_;
$x = Crypt::Perl::BigInt->from_bytes($x);
#jsrsasign avoids this when it has P and Q, which we have.
#presumably that’s because privateExponent (D) is quite large,
#so using it as an exponent is expensive.
#return $self->bmodpow($self->{'privateExponent'}, $self->{'modulus'})->as_bytes();
my $p = $self->P();
my $q = $self->Q();
my $p1 = $p->copy()->bdec();
my $q1 = $q->copy()->bdec();
my $xp = $x->copy()->bmod($p)->bmodpow( $self->D()->copy()->bmod($p1), $p );
my $xq = $x->copy()->bmod($q)->bmodpow( $self->D()->copy()->bmod($q1), $q );
#$xp->binc($p) while $xp->blt($xq);
#return ($xq + ((($xp - $xq) * $self->QINV()) % $p) * $q)->as_bytes();
my $diff = $xp->bsub($xq)->babs()->bmod($p)->bsub($p)->babs();
$diff->bmul($self->QINV())->bmod($p)->bmuladd($q, $xq)->as_bytes();
}
#----------------------------------------------------------------------
sub _sign {
my ($self, $msg, $hash_module, $hasher, $scheme) = @_;
Module::Load::load($hash_module);
my $dgst = $hash_module->can($hasher)->($msg);
my $sig;
if ($scheme eq 'PKCS1_v1_5') {
require Crypt::Perl::RSA::PKCS1_v1_5;
my $sig_length = $self->modulus_byte_length();
#The encoded length equals the length, in bytes,
#of the key’s modulus.
my $eb = Crypt::Perl::RSA::PKCS1_v1_5::encode(
$dgst,
$hasher,
$sig_length,
);
#printf "PERL: %v02x\n", $eb;
#print "mod byte length: " . Crypt::Perl::RSA::modulus_byte_length($key_obj) . $/;
my $x = Crypt::Perl::BigInt->from_hex( unpack 'H*', $eb );
$sig = $self->_transform($x)->as_bytes();
substr( $sig, 0, 0 ) = "\0" x ($sig_length - length $sig);
}
else {
die Crypt::Perl::X::create('Generic', "Unknown RSA signature scheme: “$scheme”");
}
return $sig;
}
#RSA’s signing operation.
#This function is based on _modPow() in forge’s js/rsa.js.
#
#Returns a BigInt.
sub _transform {
my ($self, $x) = @_;
my $key_bytes_length = $self->modulus_byte_length();
#cryptographic blinding
my $r;
do {
$r = Crypt::Perl::BigInt->from_hex(
Crypt::Perl::RNG::bytes_hex( $key_bytes_length ),
);
} while ($r->bge($self->N())) || ($r->bgcd($self->N())->bne(1));
$x->bmul( $r->copy()->bmodpow($self->E(), $self->N()) )->bmod($self->N());
#calculate xp and xq
my $xp = $x->copy()->bmod($self->P())->bmodpow($self->DP(), $self->P());
my $xq = $x->copy()->bmod($self->Q())->bmodpow($self->DQ(), $self->Q());
#xp must be larger than xq to avoid signed bit usage
$xp->badd($self->P()) while $xp->blt($xq);
my $y = $xp->bsub($xq)->bmul($self->QINV())->bmod($self->P());
#$y *= $self->Q();
#$y += $xq;
$y->bmuladd( $self->Q(), $xq );
#remove effect of random for cryptographic blinding
$y->bmul( $r->bmodinv($self->N()) );
$y->bmod($self->N());
return $y;
}
1;