from cryptography import x509from cryptography.hazmat.primitives import hashesfrom cryptography.hazmat.primitives.asymmetric import ec cert = x509.load_pem_x509_certificate(open("cert.pem", "rb").read()) print("subject:", cert.subject.rfc4514_string())print("issuer:", cert.issuer.rfc4514_string())print("valid:", cert.not_valid_before_utc, "->", cert.not_valid_after_utc)print("serial:", hex(cert.serial_number))print("sig alg:", cert.signature_algorithm_oid._name)print("fingerprint:", cert.fingerprint(hashes.SHA256()).hex()) key = cert.public_key()if isinstance(key, ec.EllipticCurvePublicKey): print("EC key on", key.curve.name) # secp256r1 == P-256from datetime import datetime, timezone remaining = cert.not_valid_after_utc - datetime.now(timezone.utc)print("days until expiry:", remaining.days)How it works
load_pem_x509_certificatefor PEM,load_der_x509_certificatefor binary DER — the library does not guess.- Use the
_utc-suffixed validity properties: they return timezone-aware datetimes, and the naive originals are deprecated. cert.public_key()returns the same key object types the rest of the library uses — pass it directly toverifyor serialize it to PEM.- For chain files with several
BEGIN CERTIFICATEblocks,x509.load_pem_x509_certificates(plural) parses the whole bundle into a list in one call.
Gotchas
- Comparing
not_valid_after_utcwith a naivedatetime.now()raisesTypeError— always usedatetime.now(timezone.utc). cert.signature_algorithm_oidtells you what signed this certificate (the CA's algorithm), not what the certificate's own key can do — a common misreading of ECDSA certs in RSA-signed chains.- Decoding is not validating: the library will happily parse an expired, self-signed or revoked certificate. Trust decisions need chain verification against a store.