ecdsa.com

Key fingerprint · Python

How to compute a public key fingerprint in Python

Computing a public key fingerprint in Python is hashlib applied to the right bytes: the DER-encoded SubjectPublicKeyInfo. The cryptography package produces that canonical encoding, and sha256 over it matches what Node.js, Go and OpenSSL compute for the same key.

Tested with Python 3.14 and cryptography 50.0.

Same recipe in:Node.jsPythonGoOpenSSLWebCrypto
fingerprint.py — SHA-256 over SPKI DER
import hashlib
 
from cryptography.hazmat.primitives.serialization import (
Encoding,
PublicFormat,
load_pem_public_key,
)
 
public_key = load_pem_public_key(open("pub.pem", "rb").read())
spki_der = public_key.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
 
digest = hashlib.sha256(spki_der).hexdigest()
print(":".join(digest[i:i + 2] for i in range(0, len(digest), 2)))
base64 form (HTTPS key pinning convention)
import base64
 
print(base64.b64encode(hashlib.sha256(spki_der).digest()).decode())

How it works

  1. public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo) yields the canonical binary SPKI — 91 bytes for a P-256 key.
  2. hashlib.sha256 over those bytes is the whole computation; formatting (colons, base64) is presentation only.
  3. Starting from a private key? load_pem_private_key(...).public_key() first, then the identical two lines.
  4. Starting from a certificate? x509.load_pem_x509_certificate(...).public_key() returns the same key object type, and the rest of the recipe is unchanged.

Gotchas

  • Hashing the PEM text is the classic mistake — line-ending differences alone (LF vs CRLF) would change that "fingerprint". Only DER is canonical.
  • cert.fingerprint(hashes.SHA256()) in the x509 module hashes the whole certificate, not the key — related value, different purpose, never equal to this one.
  • OpenSSH's ssh-keygen -lf fingerprints hash the SSH wire encoding and are base64 of raw digest bytes — same key, legitimately different fingerprint string.

Related recipes