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().... below

View on GitHub (pinned to fb71496164)

Solutions

  1. 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
  2. Check the key type at the call site (instanceof ECPublicKey / XECPublicKey) and log the algorithm before encrypting/decrypting
  3. If the key comes from a custom provider, wrap/convert it to a standard java.security.interfaces EC/XEC key carrying its parameters
  4. 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

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


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/86051d4ae2f64758. Report an issue: GitHub.