jwtk/jjwt · error · IllegalArgumentException
[JWA RFC 7518, Section 4.8.1.2](https://www.rfc-editor.org/r
Error message
[JWA RFC 7518, Section 4.8.1.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2) recommends password-based-encryption iterations be greater than or equal to 1000. Provided: ${iterations} What it means
RFC 7518 Section 4.8.1.2 recommends PBES2 password-based encryption use at least 1000 iterations. Pbes2HsAkwAlgorithm.assertIterations enforces this minimum and throws IllegalArgumentException for lower values (MIN_RECOMMENDED_ITERATIONS = 1000).
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithm.java:86
// new byte array indices to 0x00, meaning the last one will be our zero delimiter:
byte[] output = new byte[bytes.length + 1];
System.arraycopy(bytes, 0, output, 0, bytes.length);
return output;
}
private static int hashBitLength(int keyBitLength) {
return keyBitLength * 2;
}
private static String idFor(int hashBitLength, KeyAlgorithm<SecretKey, SecretKey> wrapAlg) {
Assert.notNull(wrapAlg, "wrapAlg argument cannot be null.");
return "PBES2-HS" + hashBitLength + "+" + wrapAlg.getId();
}
public static int assertIterations(int iterations) {
if (iterations < MIN_RECOMMENDED_ITERATIONS) {
String msg = MIN_ITERATIONS_MSG_PREFIX + iterations;
throw new IllegalArgumentException(msg);
}
return iterations;
}
public Pbes2HsAkwAlgorithm(int keyBitLength) {
this(hashBitLength(keyBitLength), new AesWrapKeyAlgorithm(keyBitLength));
}
protected Pbes2HsAkwAlgorithm(int hashBitLength, KeyAlgorithm<SecretKey, SecretKey> wrapAlg) {
super(idFor(hashBitLength, wrapAlg), "PBKDF2WithHmacSHA" + hashBitLength);
this.wrapAlg = wrapAlg; // no need to assert non-null due to 'idFor' implementation above
// There's some white box knowledge here: there is no need to assert the value of hashBitLength
// because that is done implicitly in the constructor when instantiating AesWrapKeyAlgorithm. See that class's
// implementation to see the assertion:
this.HASH_BYTE_LENGTH = hashBitLength / Byte.SIZE;
// If the JwtBuilder caller doesn't specify an iteration count, fall back to OWASP best-practice recommendationsView on GitHub (pinned to fb71496164)
Solutions
- Increase the iteration count to at least 1000 (higher, e.g. 100000+, is better for security).
- Wrap configuration in try-catch for IllegalArgumentException when accepting user-provided iteration counts.
- Set iterations via the algorithm builder's iterations() method with a validated constant.
Example fix
// before Pbes2HsAkwAlgorithm alg = new Pbes2HsAkwAlgorithm(128).iterations(10); // after Pbes2HsAkwAlgorithm alg = new Pbes2HsAkwAlgorithm(128).iterations(1000);
Defensive patterns
Strategy: validation
Validate before calling
if (iterations < 1000) {
throw new IllegalArgumentException("p2c must be >= 1000 per RFC 7518 4.8.1.2");
} Try / catch
try {
alg.iterations(n);
} catch (IllegalArgumentException e) {
// below recommended minimum: raise n
} Prevention
- Use >= 1000 iterations (more is safer)
- Never accept unvalidated user-provided p2c values
- Store iteration counts as security-reviewed constants
When it happens
Trigger: Calling iterations(n) or otherwise configuring a PBES2 JWE algorithm with a p2c/iteration count below 1000, either programmatically or via the 'p2c' header value when building.
Common situations: Copying example code with small iteration counts, tuning iterations down for speed in tests, or generating headers with p2c values from older configs.
Related errors
- JWE Header ${param} value ${iterations} exceeds ${getId()} m
- Unable to derive key
- Unexpected content JWE.
- Unexpected Claims JWE.
- PrivateKeys may not be used to encrypt data. PublicKeys are
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/5876457dbb1607f5.
Report an issue: GitHub.