Skip to content
ecdsa.com

Public key from a certificate · Python

How to extract the public key from an X.509 certificate in Python

To extract the public key from an X.509 certificate in Python, load it with the cryptography package and call cert.public_key().public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo). This recipe writes the key as SPKI PEM, computes its SHA-256 fingerprint and base64 pin, and checks that a private key belongs to the certificate.

Tested with cryptography 50.0.2 (Python 3.14.8).

extract.py — SPKI PEM, fingerprint, pin and the key-pair check
import base64
import hashlib
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
with open("cert.pem", "rb") as f:
cert = x509.load_pem_x509_certificate(f.read()) # load_der_x509_certificate for DER
public_key = cert.public_key() # an EllipticCurvePublicKey, RSAPublicKey, Ed25519PublicKey ...
spki_pem = public_key.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
spki_der = public_key.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
print(spki_pem.decode(), end="")
digest = hashlib.sha256(spki_der).digest()
print("SPKI SHA-256:", digest.hex(":"))
print("SPKI pin (base64):", base64.b64encode(digest).decode())
# Does key.pem belong to this certificate?
with open("key.pem", "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)
print("key.pem matches:", private_key.public_key() == public_key)

How it works

  1. cert.public_key() returns a typed key object (EllipticCurvePublicKey here) that can verify signatures directly.
  2. public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo) gives the BEGIN PUBLIC KEY block; with Encoding.DER it gives the bytes to hash.
  3. SHA-256 of the SPKI DER is the key fingerprint (45:ca:a7:e3:…:7b:5f); its base64 (Rcqn43J0…e18=) is the SPKI pin used by Android, OkHttp and TrustKit.
  4. private_key.public_key() == cert.public_key() compares keys by value, which tells you whether key.pem belongs to the certificate.

Gotchas

  • PublicFormat.PKCS1 exists only for RSA; on an EC key it raises ValueError: format is invalid with this key. SubjectPublicKeyInfo works for every algorithm.
  • Encoding.X962 with PublicFormat.UncompressedPoint gives the bare 65-byte point (starting with 04), not an SPKI — useful for JOSE or raw protocols, wrong for pinning or BEGIN PUBLIC KEY.
  • The certificate's own fingerprint (cert.fingerprint(hashes.SHA256()), cdb1a140… in the run) hashes the whole certificate and changes at every renewal; pin the SPKI hash instead.

Related recipes