HS256算法生成JWT签名:Linux工具与jwt.io结果不一致问题
Hey there! Let's break down why your custom Linux JWT tool's HS256 signature doesn't match what jwt.io generates with the default test data. This is a super common issue, and it almost always boils down to one of a few key implementation details. Here's what to check first:
1. Incorrect Base64URL Encoding
JWT relies on Base64URL encoding, not standard Base64. The differences are subtle but critical:
- Replace
+with- - Replace
/with_ - Strip any trailing
=padding characters
For example, the default jwt.io header {"alg":"HS256","typ":"JWT"} encodes to eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 (Base64URL), not the standard Base64 version which might include + or trailing =. If your tool uses standard Base64 for the header/payload, the input to the HMAC calculation will be wrong, leading to a mismatched signature.
2. Malformed Header-Payload Concatenation
The signature is computed over the string [base64url(header)].[base64url(payload)] — note the single dot separator with no extra spaces or characters. Double-check that your tool:
- Properly encodes both header and payload to Base64URL first
- Joins them with exactly one dot (
.) - Doesn't add newlines, spaces, or other whitespace during concatenation
Even a single extra space will change the HMAC input, resulting in a completely different signature.
3. Wrong Key Handling
HS256 uses a secret key treated as a UTF-8 encoded byte string. The default jwt.io secret is your-256-bit-secret — make sure your tool isn't mistakenly processing this key as:
- A hexadecimal string (treating each character as a byte value)
- A Base64-encoded value (decoding it before using in HMAC)
- A different character encoding (like ASCII instead of UTF-8, though this might not matter for the default secret, but could bite you later)
4. Incorrect HMAC Output Processing
After computing the HMAC-SHA256 hash of the concatenated header/payload, you need to:
- Take the raw binary output of the HMAC function (not a hexadecimal string representation)
- Encode that binary data directly to Base64URL (again, applying the
-/_replacement and stripping=)
A common mistake is converting the HMAC result to a hex string first, then encoding that string to Base64 — this will produce a totally different signature than jwt.io expects.
Quick Validation with OpenSSL
To test where your tool deviates, run this command in your Linux terminal (it replicates jwt.io's default HS256 signature process):
echo -n "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" | openssl dgst -sha256 -hmac "your-256-bit-secret" -binary | base64 | tr '+/' '-_' | tr -d '='
This should output TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ — the same signature jwt.io generates. Compare each step of your tool to this command to spot the discrepancy.
Start with these checks, and you'll almost certainly find the root cause of the signature mismatch!
内容的提问来源于stack exchange,提问作者Sonique

