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 )