Skip to content
ecdsa.com

Key fingerprint · PHP

How to compute a public key fingerprint in PHP

To compute a public key fingerprint in PHP, hash the key's DER-encoded SubjectPublicKeyInfo with SHA-256. PHP has no function that returns SPKI DER directly, but openssl_pkey_get_details gives the SPKI as PEM, and its base64 body is exactly those bytes — so the fingerprint is base64_decode plus hash, and it matches OpenSSL and every other recipe on this site.

Tested with PHP 8.5.11 (ext/openssl, OpenSSL 3.5.7) and OpenSSL 3.5.7 (openssl dgst cross-check).

spki-fingerprint.php — the fingerprint of any OpenSSL key object
<?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
}
fingerprint.php — as colon-separated hex and as a base64 pin
<?php
require '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 -c
echo base64_encode($digest), "\n"; // pin-sha256 (RFC 7469)
certificate.php — the key inside a certificate versus the certificate itself
<?php
require '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

  1. 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.
  2. 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.
  3. hash('sha256', $der, true) returns the raw digest; bin2hex with str_split gives the openssl dgst -c layout, base64_encode the pin-sha256 form.
  4. The run compares both the key's and the certificate key's digests with openssl ... -outform DER | openssl dgst -sha256.

Gotchas

  • openssl_x509_fingerprint hashes the whole certificate — a different value (54:7a:73:… for the example versus 3d: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) without true returns 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:… from ssh-keygen -lf) hash the SSH encoding of the key and never equal the SPKI value.

Related recipes