jwtk/jjwt · error · IllegalArgumentException

PrivateKeys may not be used to encrypt data. PublicKeys are

Error message

PrivateKeys may not be used to encrypt data. PublicKeys are used to encrypt, and PrivateKeys are used to decrypt.

What it means

For JWE encryption, data is encrypted with the recipient's PublicKey (asymmetric key management) — PrivateKeys decrypt. Encrypting with a PrivateKey is cryptographically incorrect, so encryptWith rejects it with an IllegalArgumentException.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java:296

        return signWith(key, alg);
    }

    @Override
    public JwtBuilder encryptWith(SecretKey key, AeadAlgorithm enc) {
        if (key instanceof Password) {
            return encryptWith((Password) key, new Pbes2HsAkwAlgorithm(enc.getKeyBitLength()), enc);
        }
        return encryptWith(key, Jwts.KEY.DIRECT, enc);
    }

    @Override
    public <K extends Key> JwtBuilder encryptWith(final K key, final KeyAlgorithm<? super K, ?> keyAlg, final AeadAlgorithm enc) {
        this.enc = Assert.notNull(enc, "Encryption algorithm cannot be null.");
        Assert.hasText(enc.getId(), "Encryption algorithm id cannot be null or empty.");

        Assert.notNull(key, "Encryption key cannot be null.");
        if (key instanceof PrivateKey) {
            throw new IllegalArgumentException(PRIV_KEY_ENC_MSG);
        }
        Assert.notNull(keyAlg, "KeyAlgorithm cannot be null.");
        final String algId = Assert.hasText(keyAlg.getId(), "KeyAlgorithm id cannot be null or empty.");

        this.key = key;
        //noinspection unchecked
        this.keyAlg = (KeyAlgorithm<Key, ?>) keyAlg;
        final KeyAlgorithm<Key, ?> alg = this.keyAlg;

        final String cekMsg = "Unable to obtain content encryption key from key management algorithm '%s'.";
        this.keyAlgFunction = Functions.wrap(alg::getEncryptionKey, SecurityException.class, cekMsg, algId);

        return this;
    }

    @Override
    public JwtBuilder compressWith(CompressionAlgorithm alg) {
        Assert.notNull(alg, "CompressionAlgorithm cannot be null");

View on GitHub (pinned to fb71496164)

Solutions

  1. Pass the recipient's PublicKey to encryptWith; keep the PrivateKey only for decryption on the recipient side.
  2. Add a guard: if (key instanceof PrivateKey) throw/redirect to the correct key before calling the builder.
  3. Fix configuration/keystore lookup so encryption code receives public keys.
  4. Catch IllegalArgumentException and log a key-role configuration error.

Example fix

// before
builder.encryptWith(myPrivateKey, Jwts.KEY.RSA_OAEP, Jwts ENC.A256GCM);
// after
builder.encryptWith(recipientPublicKey, Jwts.KEY.RSA_OAEP, Jwts.ENC.A256GCM);
Defensive patterns

Strategy: validation

Validate before calling

if (key instanceof PrivateKey) throw new IllegalArgumentException("encryptWith requires a PublicKey");

Type guard

boolean canEncrypt(Key k) { return !(k instanceof PrivateKey) && k != null; }

Try / catch

try { builder.encryptWith(key, keyAlg, enc); } catch (IllegalArgumentException e) { /* wrong key role */ }

Prevention

When it happens

Trigger: JwtBuilder.encryptWith(privateKey, keyAlg, enc) where the key argument is a java.security.PrivateKey.

Common situations: Reusing the signing (private) key object in the encryption call; mixing up encrypt/decrypt sides of an asymmetric JWE flow; keystore alias loading the wrong entry.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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