passbolt/passbolt_api · error · InvalidJwtKeyPairException
The JWT private key should be at least
Error message
The JWT private key should be at least {0} bytes long. What it means
validateKeyPair() enforces that the JWT private key is at least JWT_KEY_LENGTH (from JwtTokenCreateService, 4096-bit-derived key size) bytes. When getSecretKeySize() is below the minimum, this translated error is thrown with the required length interpolated and wrapped in InvalidJwtKeyPairException.
Solutions
- Regenerate the key pair with adequate size: sudo -u www-data bin/cake passbolt create jwt_keys (service defaults to 4096 bits)
- If generating manually, use private_key_bits >= 4096 with OPENSSL_KEYTYPE_RSA in the $config passed to JwtKeyPairService
- Confirm JwtTokenCreateService::JWT_KEY_LENGTH to know the exact required byte size in your version
- After regenerating, re-run the JWT validation step and have users re-authenticate (tokens signed with the old key are invalid)
Example fix
// before $config = ['private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA]; // after $config = ['private_key_bits' => 4096, 'private_key_type' => OPENSSL_KEYTYPE_RSA];
Defensive patterns
Strategy: validation
Validate before calling
$size = strlen(file_get_contents($privateKeyPath));
$min = \Passbolt\JwtAuthentication\Service\AccessToken\JwtTokenCreateService::JWT_KEY_LENGTH;
if ($size < $min) { // regenerate with private_key_bits >= 4096 before validation
} Try / catch
try { $service->validateKeyPair(); } catch (InvalidJwtKeyPairException $e) { // regenerate with 4096-bit RSA and re-validate } Prevention
- Always use the service defaults (4096-bit RSA) when generating
- Upgrade-time: proactively rotate old short JWT keys
- Never hand-install keys smaller than JWT_KEY_LENGTH
When it happens
Trigger: validateKeyPair() computes $secretKeySize < $minSecretKeySize — i.e. the key pair was generated with a too-small private_key_bits setting or a legacy/weak key was installed manually.
Common situations: Old passbolt instances with pre-hardening JWT keys after an upgrade that raised the minimum; hand-generated keys created with default 2048/1024-bit OpenSSL settings; custom $config passed to createKeyPair with low private_key_bits.
Related errors
- Invalid verify token expiry.
- Invalid user ID format.
- Invalid verify token format.
- The challenge is invalid. Validation Failed.
- The JWT public key could not be read or is not valid.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/adb71a6562f3579a.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/JwtAuthentication/src/Service/AccessToken/JwtKeyPairService.php:127
*/
public function validateKeyPair(?string $uuid = null): object
{
// Minimal size of the private key
$minSecretKeySize = JwtTokenCreateService::JWT_KEY_LENGTH;
$uuid = $uuid ?? UuidFactory::uuid();
try {
if (!is_readable($this->publicService->getKeyPath())) {
throw new Exception(__('The JWT public key could not be read or is not valid.'));
}
$publicKey = file_get_contents($this->publicService->getKeyPath());
$secretKeySize = $this->publicService->getSecretKeySize();
if ($secretKeySize === 0) {
throw new Exception(__('The JWT public key could not be read or is not valid.'));
}
if ($secretKeySize < $minSecretKeySize) {
throw new Exception(__(
'The JWT private key should be at least {0} bytes long.',
$this->secretService::JWT_KEY_LENGTH
));
}
$jwt = $this->secretService->createToken($uuid, '2 seconds');
return JWT::decode($jwt, new Key($publicKey, $this->secretService::JWT_ALG));
} catch (Throwable $e) {
throw new InvalidJwtKeyPairException($e->getMessage());
}
}
/**
* @return bool if a key pair exists
*/
public function keyPairExists(): bool
{View on GitHub (pinned to 31c1bbc10f)