{"record":{"id":"08c5eecb2d27709f","repo":"jwtk/jjwt","slug":"curveid-keys-may-not-be-used-with-id-digital","errorCode":null,"errorMessage":"${curveId} keys may not be used with ${id} digital signatures per https://www.rfc-editor.org/rfc/rfc8037.html#section-3.2","messagePattern":"(.+?) keys may not be used with (.+?) digital signatures per https://www\\.rfc-editor\\.org/rfc/rfc8037\\.html#section-3\\.2","errorType":"exception","errorClass":"io.jsonwebtoken.security.InvalidKeyException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/security/EdSignatureAlgorithm.java","lineNumber":74,"sourceCode":"            jcaName = EdwardsCurve.forKey(key).getJcaName();\n        }\n        return jcaName;\n    }\n\n    @Override\n    public KeyPairBuilder keyPair() {\n        return this.preferredCurve.keyPair();\n    }\n\n    @Override\n    protected void validateKey(Key key, boolean signing) {\n        super.validateKey(key, signing);\n        // should always be non-null due to algorithm name lookup, even without encoded key bytes:\n        EdwardsCurve curve = EdwardsCurve.forKey(key);\n        if (!curve.isSignatureCurve()) {\n            String msg = curve.getId() + \" keys may not be used with \" + getId() + \" digital signatures per \" +\n                    \"https://www.rfc-editor.org/rfc/rfc8037.html#section-3.2\";\n            throw new InvalidKeyException(msg);\n        }\n    }\n}\n","sourceCodeStart":56,"sourceCodeEnd":78,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/security/EdSignatureAlgorithm.java#L56-L78","documentation":"RFC 8037 §3.2 forbids signing with X25519/X448 (key-agreement-only) Edwards curves. When validating a key for an EdDSA-style signature algorithm, jjwt resolves the key's EdwardsCurve and, if that curve is not a signature curve (e.g. X25519 used with Ed25519-style signing), throws an InvalidKeyException.","triggerScenarios":"Calling Jwts.builder().signWith(x25519PrivateKey) or otherwise passing an X25519/X448 key where an Ed25519/Ed448 signing key is required.","commonSituations":"Generating the wrong key type with KeysGenerator (X25519 instead of Ed25519); confusing the two OKP curves since both use `kty: OKP`; migrating code between key agreement and signing and reusing keys.","solutions":["Generate an Ed25519 (or Ed448) key pair instead: Jwts.SIG.EdDSA.keyPair() / KeysGenerator for the signing curve.","Use the X25519 key only for key agreement (ECDH-ES JWE), not signing.","Check the key's algorithm/curve before signing and pick the algorithm that matches it."],"exampleFix":"// before\nPrivateKey key = Jwts.SIG.X25519.keyPair().getPrivate();\nJwtBuilder b = Jwts.builder().signWith(key, Jwts.SIG.EdDSA);\n// after\nPrivateKey key = Jwts.SIG.EdDSA.keyPair().getPrivate();\nJwtBuilder b = Jwts.builder().signWith(key, Jwts.SIG.EdDSA);","handlingStrategy":"validation","validationCode":"if (privateKey.getAlgorithm().startsWith(\"X\")) {\n  throw new IllegalArgumentException(privateKey.getAlgorithm() + \" keys cannot be used for signing; use Ed25519/Ed448\");\n}","typeGuard":"boolean isSignatureOkpKey(java.security.Key k) {\n  String a = k.getAlgorithm();\n  return \"Ed25519\".equals(a) || \"Ed448\".equals(a);\n}","tryCatchPattern":"try {\n  String jwt = Jwts.builder().signWith(key, Jwts.SIG.EdDSA).compact();\n} catch (InvalidKeyException e) {\n  // key is X25519/X448: regenerate with EdDSA key pair\n}","preventionTips":["Keep signing keys (Ed25519/Ed448) separate from agreement keys (X25519/X448)","Check key algorithm before signWith","Remember RFC 8037 forbids X25519/X448 signatures"],"tags":["eddsa","x25519","signature","rfc8037"],"backgroundTag":"invalid-key-type","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"}