jwtk/jjwt · error · io.jsonwebtoken.security.InvalidKeyException
keys may not be used with digital signatures per…
Error message
${curveId} keys may not be used with ${id} digital signatures per https://www.rfc-editor.org/rfc/rfc8037.html#section-3.2 What it means
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.
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.
Example fix
// before PrivateKey key = Jwts.SIG.X25519.keyPair().getPrivate(); JwtBuilder b = Jwts.builder().signWith(key, Jwts.SIG.EdDSA); // after PrivateKey key = Jwts.SIG.EdDSA.keyPair().getPrivate(); JwtBuilder b = Jwts.builder().signWith(key, Jwts.SIG.EdDSA);
Defensive patterns
Strategy: validation
Validate before calling
if (privateKey.getAlgorithm().startsWith("X")) {
throw new IllegalArgumentException(privateKey.getAlgorithm() + " keys cannot be used for signing; use Ed25519/Ed448");
} Type guard
boolean isSignatureOkpKey(java.security.Key k) {
String a = k.getAlgorithm();
return "Ed25519".equals(a) || "Ed448".equals(a);
} Try / catch
try {
String jwt = Jwts.builder().signWith(key, Jwts.SIG.EdDSA).compact();
} catch (InvalidKeyException e) {
// key is X25519/X448: regenerate with EdDSA key pair
} Prevention
- Keep signing keys (Ed25519/Ed448) separate from agreement keys (X25519/X448)
- Check key algorithm before signWith
- Remember RFC 8037 forbids X25519/X448 signatures
When it happens
Trigger: Calling Jwts.builder().signWith(x25519PrivateKey) or otherwise passing an X25519/X448 key where an Ed25519/Ed448 signing key is required.
Common situations: 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.
Related errors
- Cannot verify JWS signature: unable to locate signature…
- keys may not be used with ECDH-ES key agreement algorithms…
- Invalid ECDSA signature format.
- PrivateKeys may not be used to verify digital signatures…
- The 'none' algorithm cannot be used to create signatures.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/08c5eecb2d27709f.
Report an issue: GitHub.
Appendix: source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/EdSignatureAlgorithm.java:74
jcaName = EdwardsCurve.forKey(key).getJcaName();
}
return jcaName;
}
@Override
public KeyPairBuilder keyPair() {
return this.preferredCurve.keyPair();
}
@Override
protected void validateKey(Key key, boolean signing) {
super.validateKey(key, signing);
// should always be non-null due to algorithm name lookup, even without encoded key bytes:
EdwardsCurve curve = EdwardsCurve.forKey(key);
if (!curve.isSignatureCurve()) {
String msg = curve.getId() + " keys may not be used with " + getId() + " digital signatures per " +
"https://www.rfc-editor.org/rfc/rfc8037.html#section-3.2";
throw new InvalidKeyException(msg);
}
}
}
View on GitHub (pinned to fb71496164)