from cryptography.hazmat.primitives.serialization import ( Encoding, PublicFormat, load_pem_private_key,) private_key = load_pem_private_key(open("key.pem", "rb").read(), password=None)public_key = private_key.public_key() pem = public_key.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)open("pub.pem", "wb").write(pem)print(pem.decode(), end="") # -----BEGIN PUBLIC KEY-----from cryptography.hazmat.primitives.serialization import load_pem_public_key public_key = load_pem_public_key(open("pub.pem", "rb").read())der = public_key.public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)print(len(der), "bytes of SPKI DER") # 91 for P-256How it works
private_key.public_key()computes the public point — no key material needs to be stored twice.Encoding.PEM+PublicFormat.SubjectPublicKeyInfois the portable pairing;Encoding.DERgives the same structure in binary.PublicFormat.UncompressedPoint(withEncoding.X962) exists for protocols that want the bare04‖x‖ypoint rather than a container.
Gotchas
public_bytesrequires both arguments — there are no defaults, and mismatched pairs (PEM + UncompressedPoint) raiseValueErrorby design.- An uncompressed point (65 bytes on P-256) is not a PEM body: wrapping raw point bytes in BEGIN/END lines produces a file nothing can parse. The SPKI container adds the algorithm and curve identifiers.
- For SSH-format keys (
ecdsa-sha2-nistp256 AAAA…) useEncoding.OpenSSH+PublicFormat.OpenSSH— SPKI PEM and authorized_keys lines are different serializations of the same key.