Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.5k views
in Technique[技术] by (71.8m points)

PHP JWT validating a secret key from a given token - why don't my 2 signatures match?

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...