jwtk/jjwt · error · UnsupportedJwtException

Cannot verify JWS signature: unable to locate signature veri

Error message

Cannot verify JWS signature: unable to locate signature verification key for JWS with header: ${jwsHeader}

What it means

verifySignature() asks the configured SigningKeyResolver (or parser key configuration) for the verification key for the JWS header/payload/claims. If the resolver returns null, the parser cannot verify the signature and throws UnsupportedJwtException with this message.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:293

        try {
            algorithm = (SecureDigestAlgorithm<?, Key>) sigAlgs.apply(jwsHeader);
        } catch (UnsupportedJwtException e) {
            //For backwards compatibility.  TODO: remove this try/catch block for 1.0 and let UnsupportedJwtException propagate
            String msg = "Unsupported signature algorithm '" + alg + "': " + e.getMessage();
            throw new SignatureException(msg, e);
        }
        Assert.stateNotNull(algorithm, "JWS Signature Algorithm cannot be null.");

        //digitally signed, let's assert the signature:
        Key key;
        if (claims != null) {
            key = resolver.resolveSigningKey(jwsHeader, claims);
        } else {
            key = resolver.resolveSigningKey(jwsHeader, payload.getBytes());
        }
        if (key == null) {
            String msg = "Cannot verify JWS signature: unable to locate signature verification key for JWS with header: " + jwsHeader;
            throw new UnsupportedJwtException(msg);
        }
        Provider provider = ProviderKey.getProvider(key, this.provider); // extract if necessary
        key = ProviderKey.getKey(key); // unwrap if necessary, MUST be called after ProviderKey.getProvider
        Assert.stateNotNull(key, "ProviderKey cannot be null."); //ProviderKey impl doesn't allow null
        if (key instanceof PrivateKey) {
            throw new InvalidKeyException(PRIV_KEY_VERIFY_MSG);
        }

        final byte[] signature = decode(tokenized.getDigest(), "JWS signature");

        //re-create the jwt part without the signature.  This is what is needed for signature verification:
        InputStream payloadStream = null;
        InputStream verificationInput;
        if (jwsHeader.isPayloadEncoded()) {
            int len = tokenized.getProtected().length() + 1 + tokenized.getPayload().length();
            CharBuffer cb = CharBuffer.allocate(len);
            cb.put(Strings.wrap(tokenized.getProtected()));
            cb.put(SEPARATOR_CHAR);

View on GitHub (pinned to fb71496164)

Solutions

  1. Fix the SigningKeyResolver/keyLocator to return the correct key for the header (match on kid/issuer) or throw a descriptive exception instead of returning null
  2. Ensure the token's kid exists in your JWKS/key store — refresh keys on rotation
  3. Verify the resolver handles all expected key types/headers before returning

Example fix

// before
public Key resolveSigningKey(JwsHeader h, Claims c) {
    return keys.get(h.getKeyId()); // may be null
}
// after
public Key resolveSigningKey(JwsHeader h, Claims c) {
    Key k = keys.get(h.getKeyId());
    if (k == null) throw new SignatureException("Unknown kid: " + h.getKeyId());
    return k;
}
Defensive patterns

Strategy: validation

Validate before calling

Key key = keys.get(header.getKeyId());
if (key == null) {
    throw new SignatureException("No verification key for kid: " + header.getKeyId());
}

Try / catch

try {
    return parser.build().parseSignedClaims(token);
} catch (UnsupportedJwtException e) {
    if (e.getMessage().contains("unable to locate signature verification key")) {
        keyProvider.refresh(); // JWKS rotation
    }
    throw new UnauthorizedException(e);
}

Prevention

When it happens

Trigger: Using .keyLocator(...) or a custom SigningKeyResolver whose resolveSigningKey(header, claims/payload) returns null (e.g. unmatched kid in a JWKS lookup, unsupported key type in the resolver's switch).

Common situations: Multi-tenant JWKS key location where the token's 'kid' is not in the key set yet (rotation lag); resolver logic that returns null instead of throwing for unknown kids; parser configured without any key at all for resolver-based flows.

Related errors


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