passbolt/passbolt_api · error · InvalidJwtKeyPairException

The JWT private key could not be created.

Error message

The JWT private key could not be created.

What it means

JwtKeyPairService::createKeyPair() calls openssl_pkey_new($config) to generate the RSA JWT key pair. When OpenSSL fails to create the key resource (returns false), this error is thrown and later wrapped in InvalidJwtKeyPairException. It means the local OpenSSL library refused to generate a key with the supplied configuration.

Solutions

  1. Verify the openssl.cnf exists and OPENSSL_CONF points to it (php -i | grep openssl; check `openssl version -d`)
  2. Set a valid config in createKeyPair's $config, e.g. 'config' => a path to a working openssl.cnf with [req] distinguished_name and [rsa] sections
  3. Ensure the PHP openssl extension is loaded and functioning (php -m | grep openssl; openssl_pkey_new smoke test)
  4. Check the container image ships /etc/ssl/openssl.cnf; if not, add one

Example fix

// before
$pk = openssl_pkey_new($config); // fails: no config
// after
$config = [
  'private_key_bits' => 4096,
  'private_key_type' => OPENSSL_KEYTYPE_RSA,
  'config' => '/etc/ssl/openssl.cnf',
];
$pk = openssl_pkey_new($config);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!extension_loaded('openssl')) { throw new \RuntimeException('openssl extension missing'); }
if (!is_file('/etc/ssl/openssl.cnf')) { /* warn: OpenSSL config missing */ }

Type guard

$pk = openssl_pkey_new($config);
if (!is_resource($pk) && !($pk instanceof \OpenSSLAsymmetricKey)) { /* handle failure */ }

Try / catch

try {
    $service->createKeyPair();
} catch (\Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException $e) {
    // inspect openssl_error_string() and OPENSSL_CONF before retrying
}

Prevention

When it happens

Trigger: Running the JWT key pair generation command (execute -> createKeyPair) when openssl_pkey_new returns false, typically because the openssl.cnf path is wrong/missing or the config lacks an 'rsKeys'/'req' section (e.g. OPENSSL_CONF pointing at an empty file, default 'private_key_bits'/'private_key_type' unsupported).

Common situations: Docker/minimal images without a valid openssl.cnf; OPENSSL_CONF env var pointing to a nonexistent file; OpenSSL 3.x strictness with legacy config; passboltEmail or CLI user environment missing default openssl config.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/ecc08a33ddb706d2. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltCe/JwtAuthentication/src/Service/AccessToken/JwtKeyPairService.php:74

    public function createKeyPair(bool $force = false): void
    {
        // If pair exists but force to false, exit silently
        if ($this->keyPairExists() && !$force) {
            return;
        }

        $config = [
            'digest_alg' => JwtTokenCreateService::JWT_ALG,
            'private_key_bits' => $this->getKeyLength(),
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
        ];
        $secretKeyPath = $this->getSecretKeyPath();
        $publicKeyPath = $this->getPublicKeyPath();

        try {
            $pk = openssl_pkey_new($config);
            if ($pk === false) {
                throw new Exception('The JWT private key could not be created.');
            }
            $export = openssl_pkey_export_to_file($pk, $secretKeyPath);
            if ($export === false) {
                throw new Exception('The JWT private key could not be written.');
            }
            $publicKey = openssl_pkey_get_details($pk)['key'] ?? false;
            if ($publicKey === false) {
                throw new Exception('The JWT public key could not be extracted.');
            }
            $export = file_put_contents($publicKeyPath, $publicKey);
            if ($export === false) {
                throw new Exception('The JWT public key could not be written.');
            }

            $permission = 0640;
            $res = chmod($secretKeyPath, $permission);
            if (!$res) {
                throw new Exception("The permission of $secretKeyPath could not be set to $permission.");

View on GitHub (pinned to 31c1bbc10f)