{"record":{"id":"c9b36ccafd10e56b","repo":"jwtk/jjwt","slug":"the-keyalg-id-jwe-key-algorithm-did-not-return","errorCode":null,"errorMessage":"The '<keyAlg id>' JWE key algorithm did not return a decryption key. Unable to perform '<encAlg id>' decryption.","messagePattern":"The '<keyAlg id>' JWE key algorithm did not return a decryption key\\. Unable to perform '<encAlg id>' decryption\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java","lineNumber":558,"sourceCode":"            Key key = this.keyLocator.locate(jweHeader);\n            if (key == null) {\n                String msg = \"Cannot decrypt JWE payload: unable to locate key for JWE with header: \" + jweHeader;\n                throw new UnsupportedJwtException(msg);\n            }\n            if (key instanceof PublicKey) {\n                throw new InvalidKeyException(PUB_KEY_DECRYPT_MSG);\n            }\n\n            // extract key-specific provider if necessary;\n            Provider provider = ProviderKey.getProvider(key, this.provider);\n            key = ProviderKey.getKey(key); // this must be called after ProviderKey.getProvider\n            DecryptionKeyRequest<Key> request =\n                    new DefaultDecryptionKeyRequest<>(cekBytes, provider, null, jweHeader, encAlg, key);\n            final SecretKey cek = keyAlg.getDecryptionKey(request);\n            if (cek == null) {\n                String msg = \"The '\" + keyAlg.getId() + \"' JWE key algorithm did not return a decryption key. \" +\n                        \"Unable to perform '\" + encAlg.getId() + \"' decryption.\";\n                throw new IllegalStateException(msg);\n            }\n\n            // During decryption, the available Provider applies to the KeyAlgorithm, not the AeadAlgorithm, mostly\n            // because all JVMs support the standard AeadAlgorithms (especially with BouncyCastle in the classpath).\n            // As such, the provider here is intentionally omitted (null):\n            // TODO: add encProvider(Provider) builder method that applies to this request only?\n            InputStream ciphertext = payload.toInputStream();\n            ByteArrayOutputStream plaintext = new ByteArrayOutputStream(8192);\n            DecryptAeadRequest dreq = new DefaultDecryptAeadRequest(ciphertext, cek, aad, iv, digest);\n            encAlg.decrypt(dreq, plaintext);\n            payload = new Payload(plaintext.toByteArray(), header.getContentType());\n\n            integrityVerified = true; // AEAD performs integrity verification, so no exception = verified\n\n        } else if (hasDigest && this.signingKeyResolver == null) { //TODO: for 1.0, remove the == null check\n            // not using a signing key resolver, so we can verify the signature before reading the payload, which is\n            // always safer:\n            JwsHeader jwsHeader = Assert.stateIsInstance(JwsHeader.class, header, \"Not a JwsHeader. \");","sourceCodeStart":540,"sourceCodeEnd":576,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java#L540-L576","documentation":"After locating a raw key, the parser asks the key algorithm (e.g. RSA-OAEP, A128KW, dir) for the CEK via getDecryptionKey(request). A null CEK means the KeyAlgorithm implementation could not derive a decryption key, so the parser cannot perform the required enc decryption and throws IllegalStateException.","triggerScenarios":"The key returned by the locator is the wrong type for the key algorithm (e.g. a SecretKey where RSA-OAEP expects an RSA private key, or vice versa), or a custom KeyAlgorithm returns null from getDecryptionKey.","commonSituations":"Mixing symmetric and asymmetric algorithms (dir with a non-secret key, RSA wrap with a SecretKey), misconfigured key stores handing back the wrong key type, custom KeyAlgorithm implementations with incomplete getDecryptionKey logic.","solutions":["Match the key type to the token's alg header: SecretKey for AES key wrap/dir, RSA PrivateKey for RSA-OAEP/RSA1_5, EC PrivateKey for ECDH-ES.","Log/inspect the JWE alg and enc headers and verify the key your locator returns implements the expected algorithm interface.","If you wrote a custom KeyAlgorithm, ensure getDecryptionKey returns a non-null SecretKey for valid requests or throws a descriptive exception.","Regenerate the token with a producer configured consistently with your recipient key setup."],"exampleFix":"// before: dir alg but asymmetric key provided\nparser.decryptWith(rsaKeyPair.getPublic()).build().parse(jwe); // token alg=dir\n// after\nparser.decryptWith(aesKey).build().parse(jwe); // SecretKey for dir/A128KW","handlingStrategy":"type-guard","validationCode":"String alg = header.getJweAlgorithm().getId();\n// dir/A128KW/A192KW/A256KW require SecretKey; RSA-OAEP/RSA1_5 require RSA PrivateKey; ECDH-ES requires EC PrivateKey","typeGuard":"boolean keyMatches(String alg, Key k) {\n  return (alg.startsWith(\"A\") && k instanceof SecretKey)\n      || (alg.startsWith(\"RSA\") && k instanceof java.security.interfaces.RSAPrivateKey)\n      || (alg.startsWith(\"ECDH\") && k instanceof java.security.interfaces.ECPrivateKey);\n}","tryCatchPattern":"try { parser.parse(jwe); } catch (IllegalStateException e) { if (e.getMessage().contains(\"did not return a decryption key\")) { /* key/alg mismatch: fix locator */ } }","preventionTips":["Align issuer and recipient algorithm configuration; decode the alg header and pick keys accordingly.","Never mix symmetric tokens with asymmetric keys or vice versa.","For custom KeyAlgorithms, always return a SecretKey or throw a descriptive error — never return null.","Add tests covering each alg your system accepts."],"tags":["jwe","key-algorithm","cek","decryption"],"backgroundTag":"type-mismatch","analyzedSha":"fb71496164c71442d08adec4571d9616ed5e1b8d","analyzedAt":"2026-09-09T00:33:09.982Z","contentChangedAt":"2026-09-09T00:33:09.982Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}