{"record":{"id":"82b825a00ac0b736","repo":"apache/pulsar","slug":"algorithm-mismatch","errorCode":"ALGORITHM_MISMATCH","errorMessage":"Expected PublicKey alg [","messagePattern":"Expected PublicKey alg \\[","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-auth-oidc/src/main/java/org/apache/pulsar/broker/authentication/oidc/AuthenticationProviderOpenID.java","lineNumber":439,"sourceCode":"                case ALG_RS512:\n                    alg = Algorithm.RSA512((RSAPublicKey) publicKey, null);\n                    break;\n                case ALG_ES256:\n                    alg = Algorithm.ECDSA256((ECPublicKey) publicKey, null);\n                    break;\n                case ALG_ES384:\n                    alg = Algorithm.ECDSA384((ECPublicKey) publicKey, null);\n                    break;\n                case ALG_ES512:\n                    alg = Algorithm.ECDSA512((ECPublicKey) publicKey, null);\n                    break;\n                default:\n                    incrementFailureMetric(AuthenticationExceptionCode.UNSUPPORTED_ALGORITHM);\n                    throw new AuthenticationException(\"Unsupported algorithm: \" + publicKeyAlg);\n            }\n        } catch (ClassCastException e) {\n            incrementFailureMetric(AuthenticationExceptionCode.ALGORITHM_MISMATCH);\n            throw new AuthenticationException(\"Expected PublicKey alg [\" + publicKeyAlg + \"] does match actual alg.\");\n        }\n\n        // We verify issuer when retrieving the PublicKey, so it is not verified here.\n        // The claim presence requirements are based on https://openid.net/specs/openid-connect-basic-1_0.html#IDToken\n         Verification verifierBuilder = JWT.require(alg)\n                .acceptLeeway(acceptedTimeLeewaySeconds)\n                .withAnyOfAudience(allowedAudiences)\n                .withClaimPresence(RegisteredClaims.ISSUED_AT)\n                .withClaimPresence(RegisteredClaims.EXPIRES_AT)\n                .withClaimPresence(RegisteredClaims.SUBJECT);\n\n        if (isRoleClaimNotSubject) {\n            verifierBuilder = verifierBuilder.withClaimPresence(roleClaim);\n        }\n\n        JWTVerifier verifier = verifierBuilder.build();\n\n        try {","sourceCodeStart":421,"sourceCodeEnd":457,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-auth-oidc/src/main/java/org/apache/pulsar/broker/authentication/oidc/AuthenticationProviderOpenID.java#L421-L457","documentation":"While building the java-jwt Algorithm in verifyJWT, casting the provided PublicKey to the type implied by publicKeyAlg (e.g., (RSAPublicKey) or (ECPublicKey)) failed with ClassCastException — the key's actual type does not match the configured algorithm name. The provider throws AuthenticationException(ALGORITHM_MISMATCH) with a message that (despite the wording) means the expected and actual key types disagree.","triggerScenarios":"verifyJWT() receives a publicKey whose runtime type contradicts publicKeyAlg: an EC key while publicKeyAlg says RS256 (or vice versa), an X509/invalid key object, or a null key typed in a way that fails the cast in one of the switch's cast branches.","commonSituations":"Broker keystore/cache holds a key fetched under the wrong 'kid' so an RSA alg is paired with an EC key; IdP rotated keys and rotated to a different family (RSA -> EC) but the broker cached the old algorithm mapping; manual key configuration specifying RS256 while loading an EC .pem.","solutions":["Clear the broker's cached JWKS/keys (restart or let the cache refresh) so key 'kid' and algorithm mappings re-resolve together","Verify that the key file/keystore you configured matches the configured algorithm (RS256/384/512 require an RSA public key; ES256/384/512 require an EC P-256/P-384/P-521 key)","Check that your OIDC provider hasn't rotated signing keys to a different family; update the broker configuration accordingly","When loading keys manually, validate key.getAlgorithm() equals the configured publicKeyAlg before calling verifyJWT"],"exampleFix":"// before: EC key configured under an RSA algorithm name\npublicKeyAlg = \"RS256\"; publicKey = loadEcPem(\"idp_ec.pem\");\n// after: pair matches\npublicKeyAlg = \"ES256\"; publicKey = loadEcPem(\"idp_ec.pem\");","handlingStrategy":"validation","validationCode":"boolean algMatchesKey(String alg, PublicKey k) {\n    boolean isRsa = alg.startsWith(\"RS\");\n    boolean isEc  = alg.startsWith(\"ES\");\n    return (isRsa && k instanceof RSAPublicKey) || (isEc && k instanceof ECPublicKey);\n}","typeGuard":null,"tryCatchPattern":"try {\n    return verifyJWT(publicKey, publicKeyAlg, jwt);\n} catch (AuthenticationException e) {\n    if (e.getMessage().startsWith(\"Expected PublicKey alg\")) {\n        log.error(\"Key/algorithm mismatch: expected {} got {}\", publicKeyAlg, publicKey.getAlgorithm());\n    }\n    throw e;\n}","preventionTips":["When configuring keys manually, assert key.getAlgorithm() matches the configured name","Clear JWKS caches after IdP key rotation to avoid stale key/alg pairings","Pair 'kid' with its algorithm from the same JWKS entry; never mix entries","Alert on ClassCastException-driven auth failures — they indicate rotation or misconfiguration"],"tags":["auth","oidc","jwt","key-type","configuration"],"backgroundTag":"jwt-key-algorithm-mismatch","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}