jwtk/jjwt · error · io.jsonwebtoken.security.InvalidKeyException
Unable to determine JWA-standard Elliptic Curve for ${type}k
Error message
Unable to determine JWA-standard Elliptic Curve for ${type}key [${key}] What it means
Thrown by EcdhKeyAlgorithm.assertCurve when StandardCurves.findByKey cannot map the supplied key to any JWA-standard elliptic curve (P-256, P-384, P-521, X25519, etc.) during ECDH-ES key agreement for JWE. The key passed as the ECDH-ES encryption/decryption key is not recognized as a key on a supported curve — wrong key type, unsupported curve, or a provider key without extractable EC/XEC parameters. The message includes 'encryption ' or 'decryption ' depending on whether a PublicKey or PrivateKey was given.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java:164
}
}
@Override
protected String getJcaName(Request<?> request) {
if (request instanceof SecureRequest) {
return ((SecureRequest<?, ?>) request).getKey() instanceof ECKey ? super.getJcaName(request) : XDH_JCA_NAME;
} else {
return request.getPayload() instanceof ECKey ? super.getJcaName(request) : XDH_JCA_NAME;
}
}
private static AbstractCurve assertCurve(Key key) {
Curve curve = StandardCurves.findByKey(key);
if (curve == null) {
String type = key instanceof PublicKey ? "encryption " : "decryption ";
String msg = "Unable to determine JWA-standard Elliptic Curve for " + type + "key [" +
KeysBridge.toString(key) + "]";
throw new InvalidKeyException(msg);
}
if (curve instanceof EdwardsCurve && ((EdwardsCurve) curve).isSignatureCurve()) {
String msg = curve.getId() + " keys may not be used with ECDH-ES key agreement algorithms per " +
"https://www.rfc-editor.org/rfc/rfc8037#section-3.1.";
throw new InvalidKeyException(msg);
}
return Assert.isInstanceOf(AbstractCurve.class, curve, "AbstractCurve instance expected.");
}
@Override
public KeyResult getEncryptionKey(KeyRequest<PublicKey> request) throws SecurityException {
Assert.notNull(request, "Request cannot be null.");
JweHeader header = Assert.notNull(request.getHeader(), "Request JweHeader cannot be null.");
PublicKey publicKey = Assert.notNull(request.getPayload(), "Encryption PublicKey cannot be null.");
Curve curve = assertCurve(publicKey);
// note: we don't need to validate if specified key's point is on a supported curve here
// because that will automatically be asserted when using Jwks.builder().... belowView on GitHub (pinned to fb71496164)
Solutions
- Use an EC P-256/P-384/P-521 or X25519 key pair for ECDH-ES: generate with Jwts.SIG.EC or StandardCurves, e.g. Jwts.KEY.EC pairs
- Check the key type at the call site (instanceof ECPublicKey / XECPublicKey) and log the algorithm before encrypting/decrypting
- If the key comes from a custom provider, wrap/convert it to a standard java.security.interfaces EC/XEC key carrying its parameters
- Verify the JWA key algorithm id matches: ECDH_ES/ECDH_ES+A128KW etc. require elliptic curve keys, not octet or RSA keys
Example fix
// before: wrong key type for ECDH-ES
KeyPair kp = Jwts.KEY.RSA.keyPair().build();
Jwts.builder().encryptWith(kp.getPublic(), Jwts.KEYDIR.ECDH_ES, A256GCM);
// after: use an EC key pair
KeyPair kp = Jwts.SIG.ES256.keyPair().build(); // P-256
String jwe = Jwts.builder().encryptWith(kp.getPublic(),
Jwts.KEYDIR.ECDH_ES, Jwts.Enc.A256GCM).compact(); Defensive patterns
Strategy: type-guard
Validate before calling
// assert the key is usable for ECDH-ES before encrypting
static void requireCurveKey(Key k) {
boolean ok = k instanceof java.security.interfaces.ECPublicKey
|| k instanceof java.security.interfaces.XECPublicKey
|| k instanceof java.security.interfaces.ECPrivateKey
|| k instanceof java.security.interfaces.XECPrivateKey;
if (!ok) throw new IllegalArgumentException("ECDH-ES requires an EC or XEC key, got " + k.getAlgorithm());
} Type guard
boolean isEcdhCapableKey(Key k) {
return k instanceof java.security.interfaces.ECPublicKey
|| k instanceof java.security.interfaces.XECPublicKey
|| k instanceof java.security.interfaces.ECPrivateKey
|| k instanceof java.security.interfaces.XECPrivateKey;
} Try / catch
try {
return Jwts.parser().decryptWith(privKey).build().parseEncryptedClaims(jwe).getPayload();
} catch (InvalidKeyException e) {
throw new KeyConfigException("key not on a JWA-standard curve for ECDH-ES", e);
} Prevention
- Generate key material with JJWT (Jwts.SIG.EC keyPair / X25519) to guarantee standard curves
- Never pass RSA or SecretKey instances to ECDH-ES algorithms
- Be careful with provider-backed (HSM/PKCS11) keys that may not expose curve parameters
- Unit-test encrypt/decrypt round trips with the exact keys loaded in production
When it happens
Trigger: Calling Jwts.builder().encryptWith(key, Jwts.KEYDIR.ECDH_ES, enc) with a PublicKey on a non-standard curve, or Jwts.parser().decryptWith(key,...) with a PrivateKey JJWT cannot map — e.g. an RSA key, a raw byte[] key, a non-EC asymmetric key, or an EC key from a custom provider whose params JJWT can't read.
Common situations: Passing an RSA or secret key where an EC/X25519 public key is required for ECDH-ES; using a custom curve key not among JWA's standard curves; keys loaded from a provider (HSM/PKCS11) lacking the parameters StandardCurves.findByKey inspects; mixing up key pairs so a decryption PrivateKey of the wrong type is supplied.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ${curveId} keys may not be used with ECDH-ES key agreement a
- JWS verification key must be either a SecretKey (for MAC alg
- PublicKeys may not be used to decrypt data. PublicKeys are u
- JWE Header epk value is not an Elliptic Curve Public JWK. Va
- JWE Header epk value does not represent a point on the expec
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/86051d4ae2f64758.
Report an issue: GitHub.