lcobucci/jwt · error · Lcobucci\JWT\Signer\InvalidKeyProvided

The curve of the provided key is not

Error message

The curve of the provided key is not "{expectedCurve}", "{actualCurve}" provided

What it means

Ecdsa's guardAgainstIncompatibleCurve compares the curve name reported by openssl_pkey_get_details() (details['ec']['curve_name']) with the curve required by the concrete signer via expectedCurve(). If they differ (or the curve is unknown/null), it throws InvalidKeyProvided::incompatibleKeyCurve with the expected and actual curve names. This ensures tokens are signed on exactly the curve the algorithm mandates.

Solutions

  1. Generate the key on the exact required curve: openssl ecparam -name prime256v1 -genkey (P-256), secp384r1 (P-384), secp521r1 (P-521).
  2. Print the current curve: openssl_pkey_get_details(openssl_pkey_get_private($pem))['ec']['curve_name'] to see what you actually have.
  3. Use the signer class that matches the key's curve instead of the one you instantiated (e.g. Es384 for a secp384r1 key).
  4. When keys come from a KMS/HSM, request/export them explicitly on the standard curve the algorithm requires.

Example fix

// before
$signer = new Es256();
$key = new Key(file_get_contents('secp256k1-key.pem')); // wrong curve -> incompatibleKeyCurve

// after
shell_exec('openssl ecparam -name prime256v1 -genkey -noout -out p256-key.pem');
$signer = new Es256();
$key = new Key(file_get_contents('p256-key.pem')); // prime256v1
Defensive patterns

Strategy: validation

Validate before calling

$details = openssl_pkey_get_details(openssl_pkey_get_private($pem));
$curve = $details['ec']['curve_name'] ?? null;
if ($curve !== 'prime256v1') { // expected curve of the signer, e.g. Es256
    throw new InvalidArgumentException('EC key curve is ' . ($curve ?? 'unknown') . '; expected prime256v1');
}

Type guard

function isOnCurve(string $pem, string $expectedCurve): bool
{
    $key = openssl_pkey_get_private($pem);
    $details = $key === false ? null : openssl_pkey_get_details($key);
    return ($details['ec']['curve_name'] ?? null) === $expectedCurve;
}

Try / catch

try {
    $token = $config->builder()->getToken($signer, $key);
} catch (InvalidKeyProvided $e) {
    throw new ConfigurationException('ECDSA key curve mismatch: ' . $e->getMessage(), previous: $e);
}

Prevention

When it happens

Trigger: Calling sign()/verify() with an EC key generated on the wrong named curve — e.g. an Ed25519 or secp256k1 key passed to Es256, or a P-384 key passed to Es256 — so details['ec']['curve_name'] !== expectedCurve().

Common situations: openssl ecparam defaulting to a different curve than expected; reusing keys across signers with different curves; keys generated by cloud KMS/HSMs on non-standard curves; null curve_name when OpenSSL cannot resolve the curve (rare).

Related errors


AI-assisted analysis of lcobucci/jwt@375813049c (2026-09-14). Data as JSON: /api/errors/52a5dfe24fe46596. Report an issue: GitHub.

Appendix: source

Thrown at src/Signer/Ecdsa.php:58

                self::KEY_TYPE_MAP[OPENSSL_KEYTYPE_EC],
                self::KEY_TYPE_MAP[$type] ?? 'unknown',
            );
        }

        $expectedKeyLength = $this->expectedKeyLength();

        if ($lengthInBits !== $expectedKeyLength) {
            throw InvalidKeyProvided::incompatibleKeyLength($expectedKeyLength, $lengthInBits);
        }
    }

    /** {@inheritDoc} */
    final protected function guardAgainstIncompatibleCurve(?string $curveName): void
    {
        $expectedCurve = $this->expectedCurve();

        if ($curveName !== $expectedCurve) {
            throw InvalidKeyProvided::incompatibleKeyCurve($expectedCurve, $curveName ?? 'unknown');
        }
    }

    /**
     * @internal
     *
     * @return positive-int
     */
    abstract public function expectedKeyLength(): int;

    /**
     * Returns the name of the curve that keys must use
     *
     * @internal
     */
    abstract public function expectedCurve(): string;

    /**

View on GitHub (pinned to 375813049c)