use
constant
JWK_THUMBPRINT_DIGEST
=>
'sha256'
;
*parse_key
= \
&Crypt::Perl::PK::parse_key
;
sub
get_jwk_thumbprint {
my
(
$jwk_hr
) =
@_
;
my
$key_obj
= Crypt::Perl::PK::parse_jwk(
$jwk_hr
);
return
$key_obj
->get_jwk_thumbprint(JWK_THUMBPRINT_DIGEST());
}
*_encode_b64u
= \
&MIME::Base64::encode_base64url
;
sub
create_jwt {
my
(
%args
) =
@_
;
if
(
$args
{
'key'
}->isa(
'Crypt::Perl::RSA::PrivateKey'
)) {
return
_create_rs256_jwt(
%args
);
}
elsif
(
$args
{
'key'
}->isa(
'Crypt::Perl::ECDSA::PrivateKey'
)) {
return
_create_ecc_jwt(
%args
);
}
die
"Unrecognized “key”: “$args{'key'}”"
;
}
sub
_create_rs256_jwt {
my
(
%args
) =
@_
;
my
$alg
= JWT_RSA_SIG();
my
$key
=
$args
{
'key'
};
my
$signer_cr
=
sub
{
return
$key
->can(
"sign_$alg"
)->(
$key
,
@_
);
};
return
_create_jwt(
%args
,
alg
=>
$alg
,
signer_cr
=>
$signer_cr
,
);
}
sub
_create_ecc_jwt {
my
(
%args
) =
@_
;
my
$key
=
$args
{
'key'
};
my
$signer_cr
=
sub
{
return
$key
->sign_jwa(
@_
);
};
return
_create_jwt(
%args
,
alg
=>
$key
->get_jwa_alg(),
signer_cr
=>
$signer_cr
,
);
}
sub
_create_jwt {
my
(
%args
) =
@_
;
die
"JWS: missing 'key'"
if
!
$args
{key};
my
$payload
=
$args
{payload};
my
$alg
=
$args
{
'alg'
};
my
$header
=
$args
{extra_headers} ? { %{
$args
{extra_headers}} } : {};
$payload
= _payload_enc(
$payload
);
my
$b64u_payload
= _encode_b64u(
$payload
);
$header
->{alg} =
$alg
;
my
$json_header
= _encode_json(
$header
);
my
$b64u_header
= _encode_b64u(
$json_header
);
my
$signer_cr
=
$args
{
'signer_cr'
};
my
$b64u_signature
= _encode_b64u(
$signer_cr
->(
"$b64u_header.$b64u_payload"
,
$args
{key}) );
return
join
(
'.'
,
$b64u_header
,
$b64u_payload
,
$b64u_signature
);
}
sub
_encode_json {
my
(
$payload
) =
@_
;
return
JSON->new()->canonical(1)->encode(
$payload
);
}
sub
_payload_enc {
my
(
$payload
) =
@_
;
if
(
ref
(
$payload
) =~ /^(?:HASH|ARRAY)$/) {
$payload
= _encode_json(
$payload
);
}
else
{
utf8::downgrade(
$payload
, 1) or
die
"JWT: payload cannot contain wide character"
;
}
return
$payload
;
}
1;