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

  1. Generate an Ed25519 (or Ed448) key pair instead: Jwts.SIG.EdDSA.keyPair() / KeysGenerator for the signing curve.
  2. Use the X25519 key only for key agreement (ECDH-ES JWE), not signing.
  3. 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

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


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)