Crypt-JWS-OpenSSL
view release on metacpan or search on metacpan
lib/Crypt/JWS/OpenSSL/Util/JWK.pm view on Meta::CPAN
$kty ||= ( $jwk->{crv} ) ? 'EC' : 'RSA';
unless( $kty =~ m!\AEC\Z|\ARSA\Z!) {
croak "unsupported kty '$kty'";
}
if ( $kty eq 'EC') {
return $self->_ecc_jwk_to_pem($jwk);
} else {
return $self->_rsa_jwk_to_pem($jwk);
}
}
sub _rsa_pem_to_jwk {
my($self, $params) = @_;
my $alg = uc($params->{alg} || $params->{algo} || $params->{algorithm} || '');
if ( $alg && $alg !~ m!^(R|P)S(256|384|512)$! ) {
croak qq(invalid alg parameter '$alg');
}
my $kid = $params->{kid};
my $jwk = $self->_extract_rsa_key_members($params->{keytype}, $params->{raw_key_ref});
$jwk->{use} = 'sig';
$jwk->{kty} = 'RSA';
$jwk->{kid} = $kid;
if ($alg) {
$jwk->{alg} = $alg;
}
return $jwk;
}
sub _ecc_pem_to_jwk {
my($self, $params) = @_;
# mandatory public crv x y
# mandatory private crv x y d
# meta always add kty use alg kid
my ( $coord_len, $rx, $crv) = @{ $ECC_ALGO_MAP->{$params->{alg}} };
my $jwk = $self->_extract_ecc_key_members(
$params->{raw_key_ref}, $params->{keytype}, $rx, $coord_len, $params->{alg});
$jwk->{use} = 'sig';
$jwk->{kty} = 'EC';
$jwk->{kid} = $params->{kid};
$jwk->{alg} = $params->{alg};
$jwk->{crv} = $crv;
return $jwk;
}
sub _extract_rsa_key_members {
my($self, $keytype, $raw_key_ref) = @_;
my $rsa = ( $keytype eq 'PRIVATE' )
? Crypt::OpenSSL::RSA->new_private_key($$raw_key_ref)
: Crypt::OpenSSL::RSA->new_public_key($$raw_key_ref);
my ($n, $e, $d, $p, $q, $dp, $dq, $qi) = $rsa->get_key_parameters();
my $enc_n = (defined($n)) ? $self->encode_jwk_element($n->to_bin) : undef;
my $enc_e = (defined($e)) ? $self->encode_jwk_element($e->to_bin) : undef;
my $enc_d = (defined($d)) ? $self->encode_jwk_element($d->to_bin) : undef;
my $enc_p = (defined($p)) ? $self->encode_jwk_element($p->to_bin) : undef;
my $enc_q = (defined($q)) ? $self->encode_jwk_element($q->to_bin) : undef;
my $enc_dp = (defined($dp)) ? $self->encode_jwk_element($dp->to_bin) : undef;
my $enc_dq = (defined($dq)) ? $self->encode_jwk_element($dq->to_bin) : undef;
my $enc_qi = (defined($qi)) ? $self->encode_jwk_element($qi->to_bin) : undef;
unless(defined($enc_n) && defined($enc_e)) {
croak 'unable to extract parameters n and e from the key';
}
if ( $keytype eq 'PUBLIC' ) {
return { 'n' => $enc_n, 'e' => $enc_e };
}
unless(defined($enc_d)) {
croak 'unable to extract parameter d from the private key';
}
my $jwk_params = { 'n' => $enc_n, 'e' => $enc_e, 'd' => $enc_d };
if ( defined( $enc_p) && defined( $enc_q ) ) {
$jwk_params->{'p'} = $enc_p;
$jwk_params->{'q'} = $enc_q;
} else {
return $jwk_params;
}
if ( defined( $enc_dp )
&& defined( $enc_dq )
&& defined( $enc_qi )
) {
$jwk_params->{'dp'} = $enc_dp;
$jwk_params->{'dq'} = $enc_dq;
$jwk_params->{'qi'} = $enc_qi;
}
return $jwk_params;
}
sub _extract_ecc_key_members {
my($self, $raw_key_ref, $keytype, $rx, $coord_len, $algo) = @_;
my $jwk_params = {};
if ($keytype eq 'PRIVATE') {
if( $$raw_key_ref =~ $rx) {
my $d = $1;
$jwk_params->{'d'} = $self->encode_jwk_element($d);
} else {
croak qq(the ECC private key does not appear to be for alg type $algo);
}
}
my $raw_pub_points = substr($$raw_key_ref, - ( $coord_len * 2 ));
my $x = substr($raw_pub_points, 0, $coord_len);
my $y = substr($raw_pub_points, $coord_len, $coord_len);
$jwk_params->{'x'} = $self->encode_jwk_element($x);
$jwk_params->{'y'} = $self->encode_jwk_element($y);
return $jwk_params;
}
sub _rsa_jwk_to_pem {
my( $self, $jwk) = @_;
my ($n, $e, $d, $p, $q);
if (exists($jwk->{'n'}) && defined($jwk->{'n'})) {
$n = Crypt::OpenSSL::Bignum->new_from_bin($self->decode_jwk_element($jwk->{'n'}));
}
if (exists($jwk->{'e'}) && defined($jwk->{'e'})) {
$e = Crypt::OpenSSL::Bignum->new_from_bin($self->decode_jwk_element($jwk->{'e'}));
}
if (exists($jwk->{'d'}) && defined($jwk->{'d'})) {
$d = Crypt::OpenSSL::Bignum->new_from_bin($self->decode_jwk_element($jwk->{'d'}));
} else {
my $rsa = Crypt::OpenSSL::RSA->new_key_from_parameters( $n, $e, $d, $p, $q );
if ( $self->_version_supports_pkcs8_x509 ) {
return $rsa->get_public_key_x509_string();
} else {
return $rsa->get_public_key_string();
}
}
if (exists($jwk->{'p'}) && defined($jwk->{'p'}) && exists($jwk->{'q'}) && defined($jwk->{'q'})) {
$p = Crypt::OpenSSL::Bignum->new_from_bin($self->decode_jwk_element($jwk->{'p'}));
$q = Crypt::OpenSSL::Bignum->new_from_bin($self->decode_jwk_element($jwk->{'q'}));
}
my $rsa = Crypt::OpenSSL::RSA->new_key_from_parameters( $n, $e, $d, $p, $q );
if ( $self->_version_supports_pkcs8_x509 ) {
return $rsa->get_private_key_pkcs8_string();
} else {
return $rsa->get_private_key_string();
}
}
sub _ecc_jwk_to_pem {
my( $self, $jwk) = @_;
if ( exists( $jwk->{d} ) && defined( $jwk->{d} ) ){
return $self->_ecc_jwk_to_private_pem( $jwk );
} else {
return $self->_ecc_jwk_to_public_pem( $jwk );
}
}
sub _ecc_jwk_to_private_pem {
my( $self, $jwk) = @_;
my $curvekey = $jwk->{crv} || '';
my $curve = $self->get_ecc_curve($curvekey);
croak 'Unsupported or missing crv in JWK' if !$curve;
my $size = $curve->{size};
unless(
exists($jwk->{d}) && $jwk->{d}
&& exists($jwk->{x}) && $jwk->{x}
&& exists($jwk->{y}) && $jwk->{y}
) {
croak 'jwk does not contain all parameters d, x and y';
}
# # convert Base64 to raw bytes and left pad
my $d_bytes = $self->decode_jwk_element($jwk->{d});
my $x_bytes = $self->decode_jwk_element($jwk->{x});
my $y_bytes = $self->decode_jwk_element($jwk->{y});
my $der_ref = $self->private_ecc_key_der($curvekey, $x_bytes, $y_bytes, $d_bytes );
return $self->encode_ecc_private_key_pem($$der_ref);
}
sub _ecc_jwk_to_public_pem {
my( $self, $jwk) = @_;
my $curvekey = $jwk->{crv} || '';
my $curve = $self->get_ecc_curve($curvekey);
croak 'Unsupported or missing crv in JWK' if !$curve;
unless(
exists($jwk->{x}) && $jwk->{x}
&& exists($jwk->{y}) && $jwk->{y}
) {
croak 'jwk does not contain all parameters x and y';
}
# convert Base64 to raw bytes and left pad
my $x_bytes = $self->decode_jwk_element($jwk->{x});
my $y_bytes = $self->decode_jwk_element($jwk->{y});
my $der_ref = $self->public_ecc_key_der($curvekey, $x_bytes, $y_bytes );
return $self->encode_ecc_public_key_pem($$der_ref);
}
sub _build__version_supports_pkcs8_x509 {
my $self = shift;
my $checkversion = $self->numify_version($Crypt::OpenSSL::RSA::VERSION);
return ( $checkversion >= 0.38 ) ? 1 : 0;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Crypt::JWS::OpenSSL::Util::JWK - Utility to convert pem encoded keys to and from JWKs
=head1 VERSION
version 0.005
=head1 SYNOPSIS
use Crypt::JWS::OpenSSL::Util::JWK;
my $util = Crypt::JWS::OpenSSL::Util::JWK;
my $jwk = $util->pem_to_jwk( kid => 'my-kid-1', key => $pem_key_in );
my $pem_key_out = $util->jwk_to_pem( $jwk );
( run in 0.919 second using v1.01-cache-2.11-cpan-8dfa8b56332 )