jwtk/jjwt · error · io.jsonwebtoken.security.InvalidKeyException
JWE Header epk value is not an Elliptic Curve Public JWK. Va
Error message
JWE Header epk value is not an Elliptic Curve Public JWK. Value: ${epk} What it means
This error means the `epk` (ephemeral public key) JWK found in the JWE protected header is not an Elliptic Curve Public JWK of the type the recipient key's curve requires (an EcPublicJwk for standard EC curves, or an OctetPublicJwk for OkP/Edwards curves). During ECDH key agreement decryption, getDecryptionKey validates that the sender's ephemeral key material is a proper EC public JWK before deriving the shared key; if it is absent, malformed, or the wrong JWK family, an InvalidKeyException is thrown.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java:221
return result;
}
@Override
public SecretKey getDecryptionKey(DecryptionKeyRequest<PrivateKey> request) throws SecurityException {
Assert.notNull(request, "Request cannot be null.");
JweHeader header = Assert.notNull(request.getHeader(), "Request JweHeader cannot be null.");
PrivateKey privateKey = Assert.notNull(request.getKey(), "Decryption PrivateKey cannot be null.");
ParameterReadable reader = new RequiredParameterReader(header);
PublicJwk<?> epk = reader.get(DefaultJweHeader.EPK);
AbstractCurve curve = assertCurve(privateKey);
Assert.stateNotNull(curve, "Internal implementation state: Curve cannot be null.");
Class<?> epkClass = curve instanceof ECCurve ? EcPublicJwk.class : OctetPublicJwk.class;
if (!epkClass.isInstance(epk)) {
String msg = "JWE Header " + DefaultJweHeader.EPK + " value is not an Elliptic Curve " +
"Public JWK. Value: " + epk;
throw new InvalidKeyException(msg);
}
if (!curve.contains(epk.toKey())) {
String msg = "JWE Header " + DefaultJweHeader.EPK + " value does not represent " +
"a point on the expected curve. Value: " + epk;
throw new InvalidKeyException(msg);
}
final SecretKey derived = deriveKey(request, epk.toKey(), privateKey);
DecryptionKeyRequest<SecretKey> unwrapReq = new DefaultDecryptionKeyRequest<>(request.getPayload(),
null, request.getSecureRandom(), header, request.getEncryptionAlgorithm(), derived);
return WRAP_ALG.getDecryptionKey(unwrapReq);
}
}
View on GitHub (pinned to fb71496164)
Solutions
- Verify the sender encrypts with the matching ECDH-ES algorithm and curve for your recipient key (e.g. ECDH-ES+A256KW with an EC key on the same curve).
- Inspect the JWE protected header and confirm the `epk` field is a complete EC/OKP public JWK with kty and crv.
- Regenerate the token with a maintained jjwt (or other spec-compliant) producer rather than hand-assembling headers.
- Confirm the recipient PrivateKey curve family matches the epk curve family (EcPublicJwk vs OctetPublicJwk).
Example fix
// before: encrypting with a mismatched recipient key type Keysbuilder kb = new KeysBuilder(spec); // recipient key is an EC key but token built for OKP // after SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); // or use the EC key with ECDH-ES String jwe = Jwts.builder().setHeader(...).encryptWith(key, Jwts.KEY.Alg.ECDH_ES_A256KW, A256GCM);
Defensive patterns
Strategy: validation
Validate before calling
Object epk = header.get("epk");
boolean ok = epk instanceof Map && ((Map<?,?>) epk).get("kty") instanceof String
&& (((String)((Map<?,?>)epk).get("kty")).equals("EC") || ((String)((Map<?,?>)epk).get("kty")).equals("OKP"));
if (!ok) throw new IllegalArgumentException("JWE header missing a valid EC/OKP epk JWK"); Type guard
boolean isEcPublicJwk(Object o) {
return o instanceof Map && "EC".equals(((Map<?,?>)o).get("kty")) && ((Map<?,?>)o).containsKey("crv");
} Try / catch
try {
Jwe<Claims> jwe = Jwts.parser().decryptWith(ecPrivateKey).build().parseEncryptedClaims(token);
} catch (InvalidKeyException e) {
// epk header invalid or off-curve: reject token / request re-encryption
} Prevention
- Always encrypt with a maintained jjwt producer so epk is generated correctly
- Match the recipient key curve family to the key-management algorithm
- Never hand-edit JWE header values
When it happens
Trigger: Decrypting a JWE that was produced with an ECDH-ES key-management algorithm whose protected header contains an `epk` value that is missing, not a JSON object, not a JWK with a proper `kty`/`crv`, or whose curve family mismatches the recipient key (e.g. an octet key pair epk when decrypting with an EC P-256 private key).
Common situations: Hand-rolled token producers that omit or mangle the epk header; a sender and recipient using different curve families (EC vs OKP); tokens tampered with or truncated; using a recipient key type that does not match how the token was encrypted.
Related errors
- Unable to determine JWA-standard Elliptic Curve for ${type}k
- JWE Header epk value does not represent a point on the expec
- PublicKeys may not be used to decrypt data. PublicKeys are u
- Unable to create ${type.getSimpleName()} from JWK ${ctx}: ${
- Invalid RSA key algorithm name.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/f20042032d2e02dd.
Report an issue: GitHub.