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

  1. Verify you are using the exact key the token was signed with (same secret, or matching public key)
  2. Re-obtain the token from a trusted issuer and confirm it was not altered in transit or storage
  3. 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

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


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/6a8b8c87eaf9be83. Report an issue: GitHub.