{"record":{"id":"0aa3ef25a9703153","repo":"jwtk/jjwt","slug":"the-parsed-jwt-indicates-it-was-signed-with-the","errorCode":null,"errorMessage":"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.","messagePattern":"The parsed JWT indicates it was signed with the '(.+?)' signature algorithm, but the provided (.+?) key may not be used to verify (.+?) 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\\.","errorType":"exception","errorClass":"UnsupportedJwtException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java","lineNumber":354,"sourceCode":"            VerifySecureDigestRequest<Key> request =\n                    new DefaultVerifySecureDigestRequest<>(verificationInput, provider, null, key, signature);\n            if (!algorithm.verify(request)) {\n                String msg = \"JWT signature does not match locally computed signature. JWT validity cannot be \" +\n                        \"asserted and should not be trusted.\";\n                throw new SignatureException(msg);\n            }\n        } catch (WeakKeyException e) {\n            throw e;\n        } catch (InvalidKeyException | IllegalArgumentException e) {\n            String algId = algorithm.getId();\n            String msg = \"The parsed JWT indicates it was signed with the '\" + algId + \"' signature \" +\n                    \"algorithm, but the provided \" + key.getClass().getName() + \" key may \" +\n                    \"not be used to verify \" + algId + \" signatures.  Because the specified \" +\n                    \"key reflects a specific and expected algorithm, and the JWT does not reflect \" +\n                    \"this algorithm, it is likely that the JWT was not expected and therefore should not be \" +\n                    \"trusted.  Another possibility is that the parser was provided the incorrect \" +\n                    \"signature verification key, but this cannot be assumed for security reasons.\";\n            throw new UnsupportedJwtException(msg, e);\n        } finally {\n            Streams.reset(payloadStream);\n        }\n\n        return signature;\n    }\n\n    @Override\n    public Jwt<?, ?> parse(Reader reader) {\n        Assert.notNull(reader, \"Reader cannot be null.\");\n        return parse(reader, Payload.EMPTY);\n    }\n\n    private Jwt<?, ?> parse(Reader compact, Payload unencodedPayload) {\n\n        Assert.notNull(compact, \"Compact reader cannot be null.\");\n        Assert.stateNotNull(unencodedPayload, \"internal error: unencodedPayload is null.\");\n","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java#L336-L372","documentation":"After resolving the verification key, the parser validates that the key's algorithm is compatible with the JWS header's 'alg'. When the key reflects a specific expected algorithm that does not match the token's alg, verification is aborted with UnsupportedJwtException as a security measure against algorithm-confusion attacks.","triggerScenarios":"Token's 'alg' header (e.g. RS256 or attacker-changed alg) does not match the algorithm expected by the provided key (e.g. an HMAC SecretKey when the header says RS256, or an EC key for an RSA alg).","commonSituations":"Algorithm-confusion attempts where 'alg' was changed from RS256 to HS256 while an RSA public key bytes are used as an HMAC secret; parsers given the wrong key type for the token family; tokens re-signed by a different service with a different algorithm.","solutions":["Ensure the verification key type matches the token's alg (RSA key for RS*, EC key for ES*, SecretKey for HS*) and that the issuer signs with the same algorithm","Pin the expected algorithm in the parser (e.g. requireJwsAlgorithm or sig().add(expectedAlg)) so mismatched algs are rejected cleanly","Never verify HMAC with raw public-key bytes; always use the correct key class"],"exampleFix":"// before\nJws<Claims> jws = Jwts.parser()\n    .verifyWith(publicKeyBytesAsSecretKey)\n    .build().parseSignedClaims(token); // alg=RS256 vs HMAC key\n// after\nJws<Claims> jws = Jwts.parser()\n    .verifyWith(rsaPublicKey)\n    .build().parseSignedClaims(token);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    return parser.parseSignedClaims(token);\n} catch (UnsupportedJwtException e) {\n    if (e.getMessage().contains(\"may not be used to verify\")) {\n        log.warn(\"JWT alg does not match verification key algorithm\");\n    }\n    throw new UnauthorizedException(e);\n}","preventionTips":["Match key type to algorithm family (SecretKey=HS*, PublicKey RSA=RS*, EC=ES*)","Never feed RSA public key bytes into an HMAC key","Pin allowed algorithms in the parser configuration"],"tags":["jwt","algorithm-confusion","security"],"backgroundTag":"jwt-key-algorithm-mismatch","analyzedSha":"fb71496164c71442d08adec4571d9616ed5e1b8d","analyzedAt":"2026-09-09T00:33:09.982Z","contentChangedAt":"2026-09-09T00:33:09.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}