From what I can understand, it\'s a straight forward process to validate a JWT signature. But when I use some online tools to do this for me, it doesn\'t match.
I had the same problem until I figured out that I was using plain base64 encoding instead of base64url.
There are also some minor details in between.
Here is the step-by-step manual that will, hopefully, make the whole process much more clear.
Note 1: You must remove all spaces and newlines from your JSON strings (header and payload). It is implicitly done on jwt.io when you generate a JWT token.
Note 2: To convert JSON string to base64url string on cryptii.com create the following configuration:
First view: Text
Second view: Encode
Encoding: Base64
Variant: Standard 'base64url' (RFC 4648 §5)
Third view: Text
Note 3: To convert HMAC HEX code (signature) to base64url string on cryptii.com create the following configuration:
First view: Bytes
Format: Hexadecimal
Group by: None
Second view: Encode
Encoding: Base64
Variant: Standard 'base64url' (RFC 4648 §5)
Third view: Text
You are going to need only two online tools:
base64url encoding,On cryptii.com you can do both base64url encoding/decoding and also HMAC calculation, but for HMAC you need to provide a HEX key which is different from the input on jwt.io, so I used a separate service for HMAC calculation.
In this manual I used the following data:
Header:
{"alg":"HS256","typ":"JWT"}
Payload:
{"sub":"1234567890","name":"John Doe","iat":1516239022}
Secret (key):
The Earth is flat!
The secret is not base64 encoded.
Header (plain text):
{"alg":"HS256","typ":"JWT"}
Header (base64url encoded):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload (plain text):
{"sub":"1234567890","name":"John Doe","iat":1516239022}
Payload (base64url encoded):
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Calculate HMAC using SHA256 algorithm.
Input string (base64url encoded header and payload, concatenated with a dot):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Calculated code (HEX number):
c8a9ae59f3d64564364a864d22490cc666c74c66a3822be04a9a9287a707b352
The calculated HMAC code is a HEX representation of the signature.
Note: it should not be encoded to base64url as a plain text string but as a sequence of bytes.
base64url [Tool 1]:Signature (Bytes):
c8a9ae59f3d64564364a864d22490cc666c74c66a3822be04a9a9287a707b352
Signature (base64url encoded):
yKmuWfPWRWQ2SoZNIkkMxmbHTGajgivgSpqSh6cHs1I
Here are our results (all base64url encoded):
Header:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature:
yKmuWfPWRWQ2SoZNIkkMxmbHTGajgivgSpqSh6cHs1I
The results from jwt.io:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.yKmuWfPWRWQ2SoZNIkkMxmbHTGajgivgSpqSh6cHs1I
As you can see, all three parts are identical.