jwtk/jjwt · error · UnsupportedKeyException
${String.format(PUB_EXPONENT_EX_MSG, KeysBridge.toString(key
Error message
${String.format(PUB_EXPONENT_EX_MSG, KeysBridge.toString(key))} What it means
Thrown as UnsupportedKeyException from RsaPrivateJwkFactory.getPublicExponent when the given RSA private key is neither RSAPrivateCrtKey nor RSAMultiPrimePrivateCrtKey, so its public exponent cannot be read to derive/build the corresponding public JWK part. Only CRT-form RSA private keys expose the public exponent in the JCA API.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/RsaPrivateJwkFactory.java:82
RsaPrivateJwkFactory() {
super(DefaultRsaPublicJwk.TYPE_VALUE, RSAPrivateKey.class, DefaultRsaPrivateJwk.PARAMS);
}
@Override
protected boolean supportsKeyValues(JwkContext<?> ctx) {
return super.supportsKeyValues(ctx) && ctx.containsKey(DefaultRsaPrivateJwk.PRIVATE_EXPONENT.getId());
}
private static BigInteger getPublicExponent(RSAPrivateKey key) {
if (key instanceof RSAPrivateCrtKey) {
return ((RSAPrivateCrtKey) key).getPublicExponent();
} else if (key instanceof RSAMultiPrimePrivateCrtKey) {
return ((RSAMultiPrimePrivateCrtKey) key).getPublicExponent();
}
String msg = String.format(PUB_EXPONENT_EX_MSG, KeysBridge.toString(key));
throw new UnsupportedKeyException(msg);
}
private RSAPublicKey derivePublic(final JwkContext<RSAPrivateKey> ctx) {
RSAPrivateKey key = ctx.getKey();
BigInteger modulus = key.getModulus();
BigInteger publicExponent = getPublicExponent(key);
final RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
return generateKey(ctx, RSAPublicKey.class, new CheckedFunction<KeyFactory, RSAPublicKey>() {
@Override
public RSAPublicKey apply(KeyFactory kf) {
try {
return (RSAPublicKey) kf.generatePublic(spec);
} catch (Exception e) {
String msg = "Unable to derive RSAPublicKey from RSAPrivateKey " + ctx + ". Cause: " + e.getMessage();
throw new InvalidKeyException(msg);
}
}
});View on GitHub (pinned to fb71496164)
Solutions
- Load the private key with a CRT-capable KeyFactory (algorithm 'RSA' with PKCS8EncodedKeySpec usually yields RSAPrivateCrtKey; verify instanceof RSAPrivateCrtKey).
- Re-export the key so CRT parameters (prime1/prime2/exponent1/exponent2/coefficient) are included (e.g. openssl with PKCS#8 which retains CRT fields).
- Provide the matching RSAPublicKey explicitly instead of relying on derivation, or reconstruct a CRT key from stored CRT parameters.
Example fix
// before
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKey key = (RSAPrivateKey) kf.generatePrivate(spec); // may be non-CRT
// after
RSAPrivateKey key = (RSAPrivateKey) kf.generatePrivate(new PKCS8EncodedKeySpec(pkcs8Bytes));
if (!(key instanceof RSAPrivateCrtKey)) {
throw new IllegalArgumentException("CRT-form RSA private key required");
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!(privateKey instanceof RSAPrivateCrtKey) && !(privateKey instanceof RSAMultiPrimePrivateCrtKey)) throw new IllegalArgumentException("CRT-form RSA private key required"); Type guard
boolean isCrtRsaKey(Key k) { return k instanceof RSAPrivateCrtKey || k instanceof RSAMultiPrimePrivateCrtKey; } Try / catch
try { /* build JWK from private key */ } catch (UnsupportedKeyException e) { throw new IllegalStateException("Non-CRT RSA key; reload with CRT parameters", e); } Prevention
- Always load RSA private keys via PKCS8EncodedKeySpec so CRT fields are present.
- Check instanceof RSAPrivateCrtKey right after key loading.
- Keep openssl exports in PKCS#8 format which preserves CRT parameters.
When it happens
Trigger: Building a JWK from an RSAPrivateKey obtained from sources that produce non-CRT keys, e.g. RSAPrivateKey generated via certain KeyFactory/PKCS#1 'RSA' (not 'RSASSA-PSS'/CRT) specs, or keys deserialized from custom formats, when calling Jwts.builder header/JWK embedding or JwkBuilder with the private key.
Common situations: Keys loaded from PKCS#8 blobs whose KeyFactory yields plain RSAPrivateKey (two-integer) representations; keys converted through third-party crypto libraries; Android/legacy provider keys missing CRT fields.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Unrecognized JWA EC curve id '${jwaCurveId}'
- JWK Set keys[${i}]: ${e.getMessage()}
- RSA JWK 'oth' (Other Prime Info) element cannot be null.
- RSA JWK 'oth' (Other Prime Info) must contain map elements o
- RSA JWK 'oth' (Other Prime Info) element map cannot be empty
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/397f14f55f3c759c.
Report an issue: GitHub.