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

  1. Load the private key with a CRT-capable KeyFactory (algorithm 'RSA' with PKCS8EncodedKeySpec usually yields RSAPrivateCrtKey; verify instanceof RSAPrivateCrtKey).
  2. Re-export the key so CRT parameters (prime1/prime2/exponent1/exponent2/coefficient) are included (e.g. openssl with PKCS#8 which retains CRT fields).
  3. 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

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


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/397f14f55f3c759c. Report an issue: GitHub.