<?php// a path needs the file:// scheme; the second argument is the passphrase of an encrypted key$privateKey = openssl_pkey_get_private('file://' . __DIR__ . '/key.pem', getenv('KEY_PASSPHRASE') ?: null);if ($privateKey === false) { throw new RuntimeException('cannot read the private key: ' . openssl_error_string());} $message = file_get_contents('message.txt');if (!openssl_sign($message, $signature, $privateKey, OPENSSL_ALGO_SHA256)) { // the default would be SHA-1 throw new RuntimeException('signing failed: ' . openssl_error_string());} file_put_contents('sig.der', $signature); // ASN.1 DER, at most 72 bytes on P-256echo 'DER signature: ', strlen($signature), " bytes\n";echo 'base64: ', base64_encode($signature), "\n"; // for JSON or HTTP headers How it works
openssl_pkey_get_privateaccepts PEM text or afile://path, plus an optional passphrase for an encrypted key; it reads bothBEGIN PRIVATE KEYandBEGIN EC PRIVATE KEY.openssl_signwrites the signature into its second argument (by reference) and returnstrueorfalse, so the failure case is checked explicitly.OPENSSL_ALGO_SHA256makes OpenSSL hash the message with SHA-256 and sign the digest with ECDSA — the key type selects ECDSA, the constant selects the hash.- The signature is binary DER, varying in length from signature to signature (at most 72 bytes on P-256);
base64_encodemakes it safe for JSON or HTTP headers. The run verifies it withopenssl dgst -verify.
Gotchas
- Without the fourth argument
openssl_signuses SHA-1. The signature is valid, but a verifier expecting SHA-256 returns0— the most common reason PHP-made ECDSA signatures "never verify" elsewhere. - The output is DER. JWS (ES256) and WebCrypto expect the fixed 64-byte
r‖sform; convert it as in the DER ⇄ raw recipe, or a JWT library will reject the token. - Every call returns different bytes: OpenSSL draws a random nonce for each signature. Compare signatures by verifying them, never byte for byte.
- A bare path such as
'key.pem'is treated as PEM text and fails; prefix it withfile://, or passfile_get_contents().