Error message
openssl_verify(): Supplied key param cannot be coerced into a public key
Also printed as
- openssl_sign(): Supplied key param cannot be coerced into a private key
- Warning: openssl_verify(): Supplied key param cannot be coerced into a public key
The function name varies (`openssl_verify`, `openssl_sign`, `openssl_seal`, `openssl_public_encrypt`…), and so does the key kind ("public key" / "private key"). With `display_errors` on, PHP prefixes "Warning: ".
PHP OpenSSL extension — warning from openssl_verify / openssl_sign / openssl_seal
What it means
PHP's OpenSSL functions accept a key as PEM text, a `file://` path, or a key object, and the value you passed could not be turned into a key of the required kind (public for verify, private for sign). This is a warning, not an exception: the function returns `false` (or `-1`), and code that checks only for `=== 1` silently treats it as "invalid signature". The underlying OpenSSL reason is available through `openssl_error_string()`.
Why it happens
PEM with literal \n from an environment variable
commonKeys stored in `.env` files, CI variables or JSON often contain the two characters `\` and `n` instead of line breaks. OpenSSL cannot parse such a PEM.
A file path without file://
commonA bare path like `/etc/app/private.pem` is treated as PEM text and fails. PHP needs `file:///etc/app/private.pem` or the file's contents.
A certificate, private key or SSH key where a public key is expected
occasional`openssl_verify` accepts a certificate or a public key; an OpenSSH public key (`ssh-ed25519 AAAA…`) or a JWK JSON string is not a PEM.
An encrypted private key without passphrase
occasionalPassing an `ENCRYPTED PRIVATE KEY` PEM as a string to `openssl_sign` cannot work; load it with `openssl_pkey_get_private($pem, $passphrase)` first.
How to fix it
- 1.
Load the key explicitly and surface OpenSSL's reason
`openssl_pkey_get_public` / `_private` return `false` on failure; drain `openssl_error_string()` to log why, and stop instead of continuing with a failed verification.
php $pem = str_replace('\\n', "\n", getenv('JWT_PUBLIC_KEY')); // undo escaped newlines $publicKey = openssl_pkey_get_public($pem); if ($publicKey === false) { while ($msg = openssl_error_string()) { error_log($msg); // the real OpenSSL reason } throw new RuntimeException('JWT_PUBLIC_KEY is not a usable public key'); } $ok = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256) === 1; - 2.
Use file:// for paths and pass passphrases
Make the key source explicit: either the PEM contents or a `file://` URI, plus the passphrase for encrypted keys.
php // Paths need the file:// scheme; encrypted keys need their passphrase. $privateKey = openssl_pkey_get_private('file:///etc/app/private.pem', getenv('KEY_PASSPHRASE') ?: null);