jwtk/jjwt · error · SignatureException
JWT signature does not match locally computed signature. JWT
Error message
JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
What it means
The signature bytes decoded from the compact JWT do not match the signature the parser computes locally over the header+payload with the resolved key and algorithm. algorithm.verify(request) returned false, so the token is untrusted and a SignatureException is thrown.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:341
buf.put(headerBuf);
buf.put((byte) SEPARATOR_CHAR);
buf.rewind();
byte[] data = new byte[buf.remaining()];
buf.get(data);
InputStream prefixStream = Streams.of(data);
payloadStream = payload.toInputStream();
// We wrap the payloadStream here in an UncloseableInputStream to prevent the SequenceInputStream from
// closing it since we'll need to rewind/reset it if decompression is enabled
verificationInput = new SequenceInputStream(prefixStream, new UncloseableInputStream(payloadStream));
}
try {
VerifySecureDigestRequest<Key> request =
new DefaultVerifySecureDigestRequest<>(verificationInput, provider, null, key, signature);
if (!algorithm.verify(request)) {
String msg = "JWT signature does not match locally computed signature. JWT validity cannot be " +
"asserted and should not be trusted.";
throw new SignatureException(msg);
}
} catch (WeakKeyException e) {
throw e;
} catch (InvalidKeyException | IllegalArgumentException e) {
String algId = algorithm.getId();
String msg = "The parsed JWT indicates it was signed with the '" + algId + "' signature " +
"algorithm, but the provided " + key.getClass().getName() + " key may " +
"not be used to verify " + algId + " signatures. Because the specified " +
"key reflects a specific and expected algorithm, and the JWT does not reflect " +
"this algorithm, it is likely that the JWT was not expected and therefore should not be " +
"trusted. Another possibility is that the parser was provided the incorrect " +
"signature verification key, but this cannot be assumed for security reasons.";
throw new UnsupportedJwtException(msg, e);
} finally {
Streams.reset(payloadStream);
}
return signature;View on GitHub (pinned to fb71496164)
Solutions
- Verify you are using the exact key the token was signed with (same secret, or matching public key)
- Re-obtain the token from a trusted issuer and confirm it was not altered in transit or storage
- Confirm the token string is complete and unmodified (no truncation, whitespace, or encoding transformations)
Example fix
// before Jws<Claims> jws = Jwts.parser().verifyWith(oldKey).build().parseSignedClaims(token); // after SecretKey currentKey = keyStore.currentSigningKey(); // key matching the issuer's Jws<Claims> jws = Jwts.parser().verifyWith(currentKey).build().parseSignedClaims(token);
Defensive patterns
Strategy: try-catch
Try / catch
try {
return Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
} catch (SignatureException e) {
if (e.getMessage().contains("does not match locally computed signature")) {
throw new UnauthorizedException("Invalid token signature", e);
}
throw e;
} Prevention
- Keep signing and verification keys synchronized across environments
- Handle key rotation with a key set keyed by kid
- Avoid transporting tokens through channels that alter the string (encoding, wrapping)
When it happens
Trigger: Parsing a JWS signed with a different key than the verification key; token payload/header tampered with in transit; token copied with corrupted characters (e.g. trailing whitespace/newline, truncated string).
Common situations: Environment mismatch (token signed in dev with dev key, verified in prod); key rotation without JWKS refresh; manually editing a JWT's claims; storing tokens through channels that mangle Base64Url (URL-encoding, line wrapping).
Related errors
- The parsed JWT indicates it was signed with the '${algId}' s
- Unsecured JWSs (those with an alg header value of 'none') ar
- The JWS header references signature algorithm 'none' yet the
- PrivateKeys may not be used to verify digital signatures. Pr
- 'unsecuredDecompression' is only relevant if 'unsecured' is
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/6a8b8c87eaf9be83.
Report an issue: GitHub.