<?php/** SHA-256 over the DER SubjectPublicKeyInfo of a public key, private key or certificate. */function spkiFingerprint(OpenSSLAsymmetricKey $key): string{ $pem = openssl_pkey_get_details($key)['key']; // SPKI PEM, re-encoded by OpenSSL $der = base64_decode(preg_replace('/-----[^-]+-----|\s+/', '', $pem), true); return hash('sha256', $der, true); // raw 32 bytes} <?phprequire 'spki-fingerprint.php'; $key = openssl_pkey_get_public(file_get_contents('pub.pem'));if ($key === false) { throw new RuntimeException('not a public key: ' . openssl_error_string());}$digest = spkiFingerprint($key); echo implode(':', str_split(bin2hex($digest), 2)), "\n"; // the layout of openssl dgst -cecho base64_encode($digest), "\n"; // pin-sha256 (RFC 7469) <?phprequire 'spki-fingerprint.php'; // The key inside a certificate — not openssl_x509_fingerprint(), which hashes the whole certificate$cert = openssl_x509_read(file_get_contents('cert.pem'));echo 'certificate key: ', bin2hex(spkiFingerprint(openssl_pkey_get_public($cert))), "\n";echo 'certificate itself: ', openssl_x509_fingerprint($cert, 'sha256'), "\n"; How it works
openssl_pkey_get_details($key)['key']re-encodes any key — public, private or taken from a certificate — as SPKI PEM, so one function covers every input.- Stripping the armor lines and whitespace and decoding with
base64_decode(..., true)(strict mode) yields the 91-byte SPKI DER of a P-256 key. hash('sha256', $der, true)returns the raw digest;bin2hexwithstr_splitgives theopenssl dgst -clayout,base64_encodethepin-sha256form.- The run compares both the key's and the certificate key's digests with
openssl ... -outform DER | openssl dgst -sha256.
Gotchas
openssl_x509_fingerprinthashes the whole certificate — a different value (54:7a:73:…for the example versus3d:bb:28:…for its key) that changes on every renewal. Pinning needs the key fingerprint.- Hashing
file_get_contents('pub.pem')hashes PEM text, line endings included; only the DER inside is canonical across tools. hash('sha256', $der)withouttruereturns hex text; base64-encoding that text produces an 88-character string, twice as long as a real 44-character pin, that matches nothing.- OpenSSH fingerprints (
SHA256:…fromssh-keygen -lf) hash the SSH encoding of the key and never equal the SPKI value.