X.509 Certificates
An X.509 certificate is a signed public-key credential: a block of fields - subject, issuer, serial number, validity window, public key, extensions - covered by the issuer's signature. Those fields let a client check "is this key really the server's, and is it still valid?" The cryptography library can build, parse, and serialize certificates, and the OpenSSL CLI remains the standard way to run a small local CA.
Certificate Anatomy
| Field | Purpose |
|---|---|
| subject / issuer | Distinguished names of the holder and the signer |
| serialNumber | unique per issuer (random in practice) |
| notValidBefore/After | validity window |
| TBSCertificate | the fields above, hashed and signed by the issuer |
| extensions | SAN (hostnames), key usage, extended key usage, path length |
The Subject Alternative Name (SAN) extension is where modern clients check the hostname; the old CN-only check is gone from browsers and from ssl hostname verification alike.
Building a Self-Signed Certificate in Python
import datetime
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "localhost")])
now = datetime.datetime.now(datetime.timezone.utc)
cert = (
x509.CertificateBuilder()
.subject_name(name)
.issuer_name(name) # self-signed
.public_key(key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(now)
.not_valid_after(now + datetime.timedelta(days=30))
.add_extension(
x509.SubjectAlternativeName([x509.DNSName("localhost")]),
critical=False,
)
.sign(key, hashes.SHA256())
)
with open("cert.pem", "wb") as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
with open("key.pem", "wb") as f:
f.write(key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption()))
Reading and Inspecting
from cryptography import x509
cert = x509.load_pem_x509_certificate(open("cert.pem", "rb").read())
print(cert.subject.rfc4514_string())
print(cert.not_valid_before, cert.not_valid_after)
print(cert.extensions.get_extension_for_class(x509.SubjectAlternativeName))
print(cert.public_key()) # the holder's public key, for encryption or verification
A Local CA with OpenSSL
# 1. CA key + self-signed CA certificate
openssl req -x509 -newkey rsa:2048 -nodes -keyout ca.key -out ca.crt \
-days 3650 -subj "/CN=Dev CA"
# 2. server key + certificate signing request (CSR)
openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr \
-subj "/CN=localhost"
# 3. sign the CSR with the CA, adding the SAN extension
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -days 825 -out server.crt \
-addext "subjectAltName=DNS:localhost"
Trust ca.crt in your ssl context via load_verify_locations and the client from the TLS chapter now verifies the server's chain end to end: server cert signed by your CA, SAN matches the hostname, validity window holds.
Summary
Certificates are signed public keys with a validity window and a SAN. Build self-signed certs in Python for unit tests, run a small CA with OpenSSL for local development, and always check validity and SAN - that is exactly what ssl hostname verification does for you.