Crypt-JWS-OpenSSL

 view release on metacpan or  search on metacpan

MANIFEST  view on Meta::CPAN

t/keys/es256-public-key.pem
t/keys/es384-private-key.pem
t/keys/es384-public-key.pem
t/keys/es512-private-key.pem
t/keys/es512-public-key.pem
t/keys/hmac_256.secret
t/keys/hmac_384.secret
t/keys/hmac_512.secret
t/keys/jwks.json
t/keys/rsa_bad_public_key.pem
t/keys/rsa_private_key.pem
t/keys/rsa_private_key_4096.pem
t/keys/rsa_public_key.pem
t/keys/rsa_public_key_4096.pem
t/lib/CmpCryptPerl.pm
t/lib/CmpCryptPerl/Perl/ECC.pm
t/lib/CmpCryptPerl/Perl/RSA.pm
t/lib/CmpCryptX.pm
t/lib/CmpCryptX/PK/ECC.pm
t/lib/CmpCryptX/PK/RSA.pm
t/lib/token_cross.pl
xt/author/pod-syntax.t

lib/Crypt/JWS/OpenSSL/Algorithm/ECC.pm  view on Meta::CPAN

sub can_do_non_deterministic { 1 }
sub can_do_deterministic { 1 }

sub sign {
    my( $self, $request) = parse_params(@_);
    my $algorithm = $request->{algorithm};    
    my ($hash_method, $curve, $coord_len, $nid, $priv_key_qr, $hmac_method, $qlen ) = @{ $ECC_ALGO_MAP->{$algorithm} };
    
    my $key_ref = ref($request->{key});
    
    my $hex_private_key;
    
    if ( $key_ref )  {
        if ( $key_ref eq 'HASH' && exists($request->{key}->{d}) ) {
            my $keybin = $self->decode_jwk_element($request->{key}->{d});
            $hex_private_key = unpack("H*", $keybin);
        } else {
            croak 'failed to parse private key';
        }
    } else {
        $hex_private_key = $self->_load_private_key_as_hex($request->{key}, $priv_key_qr );
    }
        
    unless( $hex_private_key ) {
        croak "Could not extract a ${coord_len}-byte private key scalar from the private key";
    }
            
    my $ecc_key = Crypt::OpenSSL::EC::EC_KEY::new_by_curve_name($nid);
    die "Failed to initialize EC_KEY for curve NID $curve" unless $ecc_key;
    
    unless(Crypt::OpenSSL::EC::EC_KEY::set_private_key(
            $ecc_key, Crypt::OpenSSL::Bignum->new_from_hex($hex_private_key)
        )
    ) {
        croak "Failed to set private key on the EC_KEY object.";
    }
            
    my $digest = $hash_method->($request->{message});
    
    my $sig_obj;
        
    if ( $request->{non_deterministic} ) {

lib/Crypt/JWS/OpenSSL/Algorithm/ECC.pm  view on Meta::CPAN

            or croak "Signing failed";
    } else {
        my $group = Crypt::OpenSSL::EC::EC_GROUP::new_by_curve_name($nid);
        my $ctx   = Crypt::OpenSSL::Bignum::CTX->new();
        my $q     = Crypt::OpenSSL::Bignum->new;
        Crypt::OpenSSL::EC::EC_GROUP::get_order($group, $q, $ctx)
            or croak 'failed to initialize curve order';
        
        my $k = _generate_k(
            $q->to_hex,
            $hex_private_key,
            $digest,
            $hmac_method,
            $coord_len,
            $q->num_bits
        );
                
        # Calculate kinv
        my $q_minus_two = $q->sub(Crypt::OpenSSL::Bignum->new_from_word(2));
        my $kinv        = $k->mod_exp($q_minus_two, $q, $ctx);
        

lib/Crypt/JWS/OpenSSL/Algorithm/ECC.pm  view on Meta::CPAN

    
    my $result = Crypt::OpenSSL::ECDSA::ECDSA_do_verify( $digest, $ecdsa_sig, $ecc_key );
    
    if ( $result && $result eq '1') {
        return 1;
    } else {
        return 0;
    }
}

sub _load_private_key_as_hex {
    my($self, $pem, $rx) = @_;
    my $raw_key = $self->decode_pem($pem);
    my $hex_private_key;
    if( $raw_key =~ $rx)  {
        $hex_private_key = unpack("H*", $1);
    }
    return $hex_private_key;
}

sub _generate_k {
    my ($curve_order_hex, $private_key_hex, $digest, $hmac_method, $bytelen, $bitlen) = @_;
    my $order = Crypt::OpenSSL::Bignum->new_from_hex($curve_order_hex);
    my $key   = Crypt::OpenSSL::Bignum->new_from_hex($private_key_hex);
    
    my $curve = {
        q       => $order,
        bitlen  => $bitlen,
        bytelen => $bytelen,
    };
    
    my $privkey_bytes = $key->to_bin();
    
    substr( $privkey_bytes, 0, 0, "\x00" x ($curve->{'bytelen'} - length $privkey_bytes) );

lib/Crypt/JWS/OpenSSL/Algorithm/ECC.pm  view on Meta::CPAN


version 0.005

=head1 SYNOPSIS

  use Crypt::JWS::OpenSSL::Algorithm::ECC;
  my $jws = Crypt::JWS::OpenSSL::Algorithm::ECC->new;
  
  my $token_1 = $jws->sign(
    algorithm => 'ES384',
    key       => $secp384r1_private_key_pem_content,
    message   => join('.', $base64urlEncodedHeader, $base64urlEncodedClaims),
  );
  
  my $token2 = $jws->sign(
    algorithm     => 'ES256',
    key           => $prime256v1_private_key_pem_content,
    message       => join('.', $base64urlEncodedHeader, $base64urlEncodedClaims),
    deterministic => 1
  );
  
  my $is_verified_1 = $jws->verify(
    algorithm => 'ES384',
    key       => $secp384r1_public_key_pem_content,
    message   => join('.', $base64urlEncodedHeader, $base64urlEncodedClaims),
    signature => $base64urlDecodedSignature,
  );

lib/Crypt/JWS/OpenSSL/Algorithm/RSA.pm  view on Meta::CPAN


sub sign {
    my( $self, $request) = parse_params(@_);
    my $algorithm = $request->{algorithm};
      
    my $keyref = ref( $request->{key} );
    my $rsa;
    
    if ($keyref) {
        if ( $keyref eq 'HASH' ) {
            my $keyparams = $self->_get_private_key_params( $request->{key} );
            $rsa = Crypt::OpenSSL::RSA->new_key_from_parameters( @$keyparams, 'check' => 1 );
        } else {
            croak 'invalid jwk reference';
        }
    } else {
        $rsa = Crypt::OpenSSL::RSA->new_private_key($request->{key});
    }
    for my $method ( @{ $RSA_ALGO_MAP->{$algorithm} } ) {
        $rsa->$method;
    }
    return $rsa->sign($request->{message});
}

sub verify {
    my( $self, $request) = parse_params(@_);
    my $algorithm = $request->{algorithm};

lib/Crypt/JWS/OpenSSL/Algorithm/RSA.pm  view on Meta::CPAN

        }
    } else {
        $rsa = Crypt::OpenSSL::RSA->new_public_key($request->{key});
    }
    for my $method ( @{ $RSA_ALGO_MAP->{$algorithm} } ) {
        $rsa->$method;
    }
    return $rsa->verify($request->{message}, $request->{signature}) ? 1 : 0;
}

sub _get_private_key_params {
    my( $self, $jwk) = @_;
    my ($n, $e, $d, $p, $q );
    
    my @bignums = ();
    
    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'})) {

lib/Crypt/JWS/OpenSSL/Algorithm/RSA.pm  view on Meta::CPAN


version 0.005

=head1 SYNOPSIS

  use Crypt::JWS::OpenSSL::Algorithm::RSA;
  my $jws = Crypt::JWS::OpenSSL::Algorithm::RSA->new;
  
  my $token = $jws->sign(
    algorithm => 'RS256',
    key       => $rsa_private_key_pem_content,
    message   => join('.', $base64urlEncodedHeader, $base64urlEncodedClaims),
  );
    
  my $is_verified = $jws->verify(
    algorithm => 'RS256',
    key       => $rsa_public_key_pem_content,
    message   => join('.', $base64urlEncodedHeader, $base64urlEncodedClaims),
    signature => $base64urlDecodedSignature,
  );

lib/Crypt/JWS/OpenSSL/Role/Encoder.pm  view on Meta::CPAN

    $base64 =~ tr[-_][+/];
    $base64 .= '=' while length($base64) % 4;
    return $self->decode_base64($base64);
}

sub decode_base64 {
    my($self, $base64) = @_;
    return MIME::Base64::decode_base64($base64);
}

sub encode_rsa_private_key_pem {
    my($self, $bytes) = @_;
    return $self->_encode_pem(\$bytes, 'PRIVATE KEY');
}

sub encode_rsa_public_key_pem {
    my($self, $bytes) = @_;
    return $self->_encode_pem(\$bytes, 'PUBLIC KEY');
}

sub encode_ecc_private_key_pem {
    my($self, $bytes) = @_;
    return $self->_encode_pem(\$bytes, 'EC PRIVATE KEY');
}

sub encode_ecc_public_key_pem {
    my($self, $bytes) = @_;
    return $self->_encode_pem(\$bytes, 'PUBLIC KEY');
}

sub _encode_pem {

lib/Crypt/JWS/OpenSSL/Role/Encoder.pm  view on Meta::CPAN

Accepts a Base64 URL encoded string and returns the raw decoded bytes.

=head2 encode_jwk_element

Accepts raw bytes and returns the Base64 URL encoded result.

=head2 decode_jwk_element

Accepts a Base64 URL encoded string and returns the raw decoded bytes.

=head2 encode_rsa_private_key_pem

Accepts a der encoded RSA private key and returns the pem encoded result.

=head2 encode_rsa_public_key_pem

Accepts a der encoded RSA public key and returns the pem encoded result.

=head2 encode_ecc_private_key_pem

Accepts a der encoded EC private key and returns the pem encoded result.

=head2 encode_ecc_public_key_pem

Accepts a der encoded EC public key and returns the pem encoded result.

=head2 decode_pem

Decodes a pem format key and returns the der result.

lib/Crypt/JWS/OpenSSL/Util/JWK.pm  view on Meta::CPAN

    $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;

lib/Crypt/JWS/OpenSSL/Util/JWK.pm  view on Meta::CPAN

    }
    
    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 );
    }

lib/Crypt/JWS/OpenSSL/Util/JWK.pm  view on Meta::CPAN

        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);
    

lib/Crypt/JWS/OpenSSL/Util/PEM.pm  view on Meta::CPAN

    
    if ($bits > 4096 ) {
        croak 'maximum size of RSA key supported is 4096 bits';
    }
    
    my $rsa = Crypt::OpenSSL::RSA->generate_key($bits);
    
    my ( $private_pem, $public_pem );
    
    if ( $self->_version_supports_pkcs8_x509 ) {
        $private_pem = $rsa->get_private_key_pkcs8_string();
        $public_pem = $rsa->get_public_key_x509_string();
    } else {
        $private_pem = $rsa->get_private_key_string();
        $public_pem = $rsa->get_public_key_string();
    }
    
    return ( wantarray ) ? ( $private_pem, $public_pem ) : [ $private_pem, $public_pem ];
}

sub generate_ecc_key_pair {
    my( $self, $alg) = @_;
    
    $alg ||= 'none';

lib/Crypt/JWS/OpenSSL/Util/PEM.pm  view on Meta::CPAN

    
    my $nid = $ECC_NID_MAP->{$alg};
    
    my $group = Crypt::OpenSSL::EC::EC_GROUP::new_by_curve_name($nid);
    my $ctx   = Crypt::OpenSSL::Bignum::CTX->new();
    my $order = Crypt::OpenSSL::Bignum->zero;
    $group->get_order( $order, $ctx );
    my $eckey = Crypt::OpenSSL::EC::EC_KEY::new();
    $eckey->set_group($group);
    $eckey->generate_key();
    my $d = $eckey->get0_private_key();
    my $p = $eckey->get0_public_key();
    
    my $x = Crypt::OpenSSL::Bignum->new;
    my $y = Crypt::OpenSSL::Bignum->new;
    
    Crypt::OpenSSL::EC::EC_POINT::get_affine_coordinates_GFp($group, $p, $x, $y, $ctx);
    
    my $private_ref = $self->private_ecc_key_der($alg, $x->to_bin, $y->to_bin, $d->to_bin);
    my $private_pem = $self->encode_ecc_private_key_pem($$private_ref);
    my $public_ref  = $self->public_ecc_key_der($alg, $x->to_bin, $y->to_bin);
    my $public_pem =  $self->encode_ecc_public_key_pem($$public_ref);
    
    return ( wantarray ) ? ( $private_pem, $public_pem ) : [ $private_pem, $public_pem ];
}

sub _build__version_supports_pkcs8_x509 {
    my $self = shift;
    my $checkversion = $self->numify_version($Crypt::OpenSSL::RSA::VERSION);
    return ( $checkversion >= 0.38 ) ? 1 : 0;   

t/10_basic.t  view on Meta::CPAN

            secret      => $pubkey 
        );
        
        is($verified, 0, qq($algo - bad verify token fails in correct part of code));
    }

}

## RSA TESTS
{
    my $prvkey2048 = load_key('t/keys/rsa_private_key.pem');
    my $pubkey2048 = load_key('t/keys/rsa_public_key.pem');
    my $prvkey4096 = load_key('t/keys/rsa_private_key_4096.pem');
    my $pubkey4096 = load_key('t/keys/rsa_public_key_4096.pem');
    
    my $bit_key_map = {
        '2048' => { public => $pubkey2048, private => $prvkey2048 },
        '4096' => { public => $pubkey4096, private => $prvkey4096 },
    };
    
    my $badrsakey = load_key('t/keys/rsa_bad_public_key.pem');
    
    my @pkcs1algos = qw( RS256 RS384 RS512 );

t/12_jwks.t  view on Meta::CPAN


my $inputclaims = {
    'sub'   => 'Test subject',
    'roles' => 'testrole',
    'exp'   => 1786935249,
    'iss'   => 'this/test/file'
};

## RSA TESTS
{
    my $prvkey2048 = load_key('t/keys/rsa_private_key.pem');
    my $pubkey2048 = load_key('t/keys/rsa_public_key.pem');
    my $prvkey4096 = load_key('t/keys/rsa_private_key_4096.pem');
    my $pubkey4096 = load_key('t/keys/rsa_public_key_4096.pem');
    
    my $prvjwk2048 = get_jwk('id-rsa-2048-private');
    my $pubjwk2048 = get_jwk('id-rsa-2048-public');
    my $prvjwk4096 = get_jwk('id-rsa-4096-private');
    my $pubjwk4096 = get_jwk('id-rsa-4096-public');
    
    my $bit_key_map = {
        '2048' => {
            public      => $pubkey2048,

t/lib/token_cross.pl  view on Meta::CPAN

my $inputclaims = {
    'sub'   => 'Test subject',
    'roles' => 'testrole',
    'exp'   => 1786935249,
    'iss'   => 'this/test/file'
};


## RSA TESTS
{
    my $prvkey2048 = load_key('t/keys/rsa_private_key.pem');
    my $pubkey2048 = load_key('t/keys/rsa_public_key.pem');
    my $prvkey4096 = load_key('t/keys/rsa_private_key_4096.pem');
    my $pubkey4096 = load_key('t/keys/rsa_public_key_4096.pem');
    
    my $bit_key_map = {
        '2048' => { public => $pubkey2048, private => $prvkey2048 },
        '4096' => { public => $pubkey4096, private => $prvkey4096 },
    };
    
    my $badrsakey = load_key('t/keys/rsa_bad_public_key.pem');
    
    my @pkcs1algos = qw( RS256 RS384 RS512 );



( run in 1.105 second using v1.01-cache-2.11-cpan-8dfa8b56332 )