Reputation: 2364
I am using PHP to parse a JWT Token that is returned from an external API. It's easy enough to parse the payload, but I want to first check that the Secret Key used in the token matches the Secret Key on my server. This will act as my handshake. To do this I will recreate the signature part of the token (3rd part) using the Secret Key that I think has been used. If the resulting value matches then I know the Secret Keys match.
As an example, my Secret Key is: testsecretkey
The Token returned to me is:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjIzMTc2MiIsIklkIjoiMTM0IiwiSWRQZXJzb24iOiIxMTkiLCJQZXJzb25lblJvbGxlbiI6Ilt7XCJSb2xsZVwiOjEsXCJJZFwiOjE0MTg5fSx7XCJSb2xsZVwiOjIsXCJJZFwiOjExOX0se1wiUm9sbGVcIjozLFwiSWRcIjoxMDY5MH1dIiwibmJmIjoxNjEwNDYxNjA2LCJleHAiOjE2MTA0NjUyMDYsImlhdCI6MTYxMDQ2MTYwNn0.Cqz2mtZ9g_L4BSYTQztzQ4W0VldAf-Ihok24jVVi8iE
If I run that through https://jwt.io/ and enter "testscretkey" in the "Verify Signature" box and click the "secret base64 encoded" checkbox, everything checks out, and the token looks correct.
Back on my server in PHP, I split the token in to 3 parts ($tokenParts) using "." as a separator. The encoded signature (part 3, the last part of the token above) shows...:
Cqz2mtZ9g_L4BSYTQztzQ4W0VldAf-Ihok24jVVi8iE
I apply my Secret Key encryption to token parts 1 and 2:
$base64UrlSignature = base64_encode(
hash_hmac(
'sha256', //The correct enc type
$tokenParts[0] . "." . $tokenParts[1], //The first 2 parts of the token
'testsecretkey', // The secret key that should have been used
true
)
);
...then I compare my new signature ($base64UrlSignature) with the original encoded signature ($tokenParts[2]). If both match then the handshake is confirmed.
However, the values don't match even though I've used the same key. Instead my new signature is:
K4Mx71y1yRdQ7xotyR78FzQU8J2xTc65wZbhGFE-s4s
Returning back to jwt.io (with the original token information still entered in the box) if I UNTICK the "secret base64 encoded" checkbox I can see the the JWT token signature (in blue) changes to the same value as my new signature ($base64UrlSignature). But this makes no sense to me, because I have base64_encoded my new signature.
Can anyone help me figure out where I've gone wrong?
Thanks
Upvotes: 1
Views: 1346
Reputation: 1457
The "secret base64 encoded" means that the secret you are entering is base64-encoded. Of course, the secret you are entering ("testsecretkey") is not really base64, but it still validly decodes.
To simulate this in PHP:
function base64_encode_url($string) {
return str_replace(['+','/','='], ['-','_',''], base64_encode($string));
}
function base64_decode_url($string) {
return base64_decode(str_replace(['-','_'], ['+','/'], $string));
}
$signature = base64_encode_url(
hash_hmac(
"sha256",
$tokenParts[0] . "." . $tokenParts[1],
base64_decode_url("testsecretkey"),
true
)
);
This code yields Cqz2mtZ...
, matching the signature you got in the response. Note that you must use a special modified version of base64 which is guaranteed to only generate URL-safe characters.
The API you are getting this response from may have expected you to enter a base64 value for the secret key instead of a plaintext string.
Upvotes: 2