{"record":{"id":"62cd68490261fdcd","repo":"jwtk/jjwt","slug":"curveid-keys-may-not-be-used-with-ecdh-es-key-a","errorCode":null,"errorMessage":"${curveId} keys may not be used with ECDH-ES key agreement algorithms per https://www.rfc-editor.org/rfc/rfc8037#section-3.1.","messagePattern":"(.+?) keys may not be used with ECDH-ES key agreement algorithms per https://www\\.rfc-editor\\.org/rfc/rfc8037#section-3\\.1\\.","errorType":"exception","errorClass":"io.jsonwebtoken.security.InvalidKeyException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java","lineNumber":169,"sourceCode":"        if (request instanceof SecureRequest) {\n            return ((SecureRequest<?, ?>) request).getKey() instanceof ECKey ? super.getJcaName(request) : XDH_JCA_NAME;\n        } else {\n            return request.getPayload() instanceof ECKey ? super.getJcaName(request) : XDH_JCA_NAME;\n        }\n    }\n\n    private static AbstractCurve assertCurve(Key key) {\n        Curve curve = StandardCurves.findByKey(key);\n        if (curve == null) {\n            String type = key instanceof PublicKey ? \"encryption \" : \"decryption \";\n            String msg = \"Unable to determine JWA-standard Elliptic Curve for \" + type + \"key [\" +\n                    KeysBridge.toString(key) + \"]\";\n            throw new InvalidKeyException(msg);\n        }\n        if (curve instanceof EdwardsCurve && ((EdwardsCurve) curve).isSignatureCurve()) {\n            String msg = curve.getId() + \" keys may not be used with ECDH-ES key agreement algorithms per \" +\n                    \"https://www.rfc-editor.org/rfc/rfc8037#section-3.1.\";\n            throw new InvalidKeyException(msg);\n        }\n        return Assert.isInstanceOf(AbstractCurve.class, curve, \"AbstractCurve instance expected.\");\n    }\n\n    @Override\n    public KeyResult getEncryptionKey(KeyRequest<PublicKey> request) throws SecurityException {\n        Assert.notNull(request, \"Request cannot be null.\");\n        JweHeader header = Assert.notNull(request.getHeader(), \"Request JweHeader cannot be null.\");\n        PublicKey publicKey = Assert.notNull(request.getPayload(), \"Encryption PublicKey cannot be null.\");\n\n        Curve curve = assertCurve(publicKey);\n        // note: we don't need to validate if specified key's point is on a supported curve here\n        // because that will automatically be asserted when using Jwks.builder().... below\n        Assert.stateNotNull(curve, \"Internal implementation state: Curve cannot be null.\");\n\n        // Generate our ephemeral key pair:\n        final SecureRandom random = ensureSecureRandom(request);\n        DynamicJwkBuilder<?, ?> jwkBuilder = Jwks.builder().random(random);","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java#L151-L187","documentation":"Thrown by EcdhKeyAlgorithm.assertCurve when the resolved curve is an EdwardsCurve that is a signature curve (Ed25519 or Ed449), blocking its use with ECDH-ES key agreement. RFC 8037 section 3.1 mandates that the OKP keys usable for ECDH-ES are the X25519/X448 key agreement types, not the Ed25519/Ed449 signature types, even though both share curve IDs — so JJWT rejects signing keys presented for encryption. You must use X25519/X448 keys for ECDH-ES.","triggerScenarios":"Calling encryptWith(ed25519PublicKey, Jwts.KEYDIR.ECDH_ES/ECDH_ES+AxxxKW, enc) or decryptWith an Ed25519/Ed449 private key — i.e. using an EdDSA signing key pair (e.g. generated via Jwts.SIG.EdDSA or an OKP JWK with crv=Ed25519) as the ECDH-ES key agreement key.","commonSituations":"Generating one OKP key pair and trying to use it for both JWT signing (EdDSA) and JWE encryption (ECDH-ES); confusing Ed25519 and X25519 since both are '25519' curves; migrating configs that specify a single curve id for all OKP keys.","solutions":["Generate a separate X25519 key pair for encryption: Jwts.KEY.X25519? or Keys for XDH — use JJWT's X25519 curve keyPair utilities for ECDH-ES","Keep Ed25519 keys exclusively for JWS signing (EdDSA) and X25519/X448 keys exclusively for ECDH-ES key agreement","If loading OKP JWKs, ensure the crv/kty actually denotes X25519 (key-agreement OKP), not Ed25519 (signature OKP)","Catch InvalidKeyException around encryption/decryption to surface a clear configuration error to callers"],"exampleFix":"// before: reusing Ed25519 signing keys for encryption\nKeyPair ed = Jwts.SIG.EdDSA.keyPair().build();\nJwts.builder().encryptWith(ed.getPublic(), Jwts.KEYDIR.ECDH_ES, enc);\n// after: dedicated X25519 agreement keys\nKeyPair x25519 = Jwts.KEY.X25519.keyPair().build();\nString jwe = Jwts.builder().encryptWith(x25519.getPublic(),\n    Jwts.KEYDIR.ECDH_ES, Jwts.Enc.A256GCM).compact();","handlingStrategy":"validation","validationCode":"// ensure curve is an agreement curve, not a signature curve, before ECDH-ES\nstatic void requireAgreementCurve(Key k) {\n    String alg = k.getAlgorithm();\n    if (alg != null && (alg.contains(\"Ed25519\") || alg.contains(\"Ed448\") || alg.equals(\"EdDSA\"))) {\n        throw new IllegalArgumentException(\"Use X25519/X448 keys for ECDH-ES, not \" + alg);\n    }\n}","typeGuard":"boolean isKeyAgreementOkp(Key k) {\n    String a = k.getAlgorithm();\n    return a != null && (a.equals(\"XDH\") || a.contains(\"X25519\") || a.contains(\"X448\"));\n}","tryCatchPattern":"try {\n    return Jwts.builder().encryptWith(pub, Jwts.KEYDIR.ECDH_ES, enc).compact();\n} catch (InvalidKeyException e) {\n    throw new KeyConfigException(\"Ed25519/Ed449 keys cannot be used with ECDH-ES (RFC 8037 §3.1)\", e);\n}","preventionTips":["Maintain separate key pairs: Ed25519 for signing, X25519/X448 for encryption","When loading OKP JWKs, verify crv is X25519/X448 (not Ed25519/Ed449) for ECDH-ES consumers","Document key usage per algorithm in your key management config to avoid curve mix-ups","Add a startup check that decrypt keys resolve to agreement curves before serving traffic"],"tags":["jwt","jwe","ecdh","ed25519","rfc8037","key-management"],"backgroundTag":"invalid-argument-value","analyzedSha":"fb71496164c71442d08adec4571d9616ed5e1b8d","analyzedAt":"2026-09-09T00:33:09.982Z","contentChangedAt":"2026-09-09T00:33:09.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}