Certificates and PKI: Building Trust on the Internet
1. Why Should You Care?
You visit https://bank.example.com. Your browser shows a padlock. How does it know this is really your bank and not an attacker?
The answer is certificatesโdigital documents that bind public keys to identities. Without them, HTTPS would be meaningless.
Understanding PKI helps you:
- Debug TLS certificate errors
- Set up HTTPS for your applications
- Understand why โjust click through the warningโ is dangerous
- Know when and how to use client certificates
2. Definition
A digital certificate (specifically X.509) binds:
- A public key
- An identity (domain name, organization, etc.)
- A signature from a trusted authority
Public Key Infrastructure (PKI) is the system of:
- Certificate Authorities (CAs) that issue certificates
- Policies for issuing and revoking certificates
- Trust stores containing trusted root certificates
Certificate = "I, Trusted CA, certify that
public key XYZ belongs to bank.example.com"
+ CA's signature3. Whatโs Inside a Certificate
X.509 Certificate Structure
Certificate:
Version: 3
Serial Number: 04:00:00:00:00:01:2F:4E:E1:5B:3D
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=DigiCert Global Root CA
Validity:
Not Before: Mar 08 12:00:00 2023 GMT
Not After: Mar 08 12:00:00 2024 GMT
Subject: CN=www.example.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
EC Public Key:
pub: 04:AB:CD:...
ASN1 OID: prime256v1
X509v3 Extensions:
Subject Alternative Name:
DNS:www.example.com
DNS:example.com
Basic Constraints:
CA:FALSE
Key Usage:
Digital Signature, Key Encipherment
Extended Key Usage:
TLS Web Server AuthenticationKey Fields Explained
Subject: Who the certificate is for
CN=www.example.com (Common Name)
O=Example Inc (Organization)
Issuer: Who signed the certificate
The CA that vouches for this identity
Validity: When the certificate is valid
Not Before / Not After dates
Subject Alternative Name (SAN):
Additional domains covered
Modern browsers require this
Key Usage:
What the key can be used for
Digital Signature, Key Encipherment, etc.
Extended Key Usage:
More specific purposes
TLS Web Server Authentication
Code Signing, etc.4. The Chain of Trust
How Trust Works
Root CA (Self-signed, in browser trust store)
โ
โ signs
โผ
Intermediate CA Certificate
โ
โ signs
โผ
End-Entity Certificate (your website)
Browser verifies:
1. End certificate signed by intermediate
2. Intermediate signed by root
3. Root is in trusted store
4. All certificates are valid (not expired, not revoked)Why Intermediates?
Security: Root keys are extremely valuable
- Stored in offline HSMs
- Used rarely (to sign intermediates)
- If compromised, entire PKI collapses
Flexibility:
- Intermediates can be revoked if compromised
- Different intermediates for different purposes
- Shorter validity periods
Defense in depth:
- One more layer to compromise
- Can rotate intermediates without changing roots5. Certificate Authorities (CAs)
What CAs Do
1. Verify identity of certificate requestor
- Domain Validation (DV): Prove you control domain
- Organization Validation (OV): Verify organization exists
- Extended Validation (EV): Extensive legal verification
2. Issue certificates
- Sign with CA's private key
- Include appropriate constraints
3. Maintain revocation information
- CRL (Certificate Revocation List)
- OCSP (Online Certificate Status Protocol)
4. Protect their keys
- Root keys in HSMs
- Strict access controls
- Regular auditsMajor CAs
Commercial:
- DigiCert
- Sectigo (formerly Comodo)
- GlobalSign
- Entrust
Free:
- Let's Encrypt (automated DV certificates)
- ZeroSSL
Private:
- Your organization's internal CA
- For internal services, client certificates6. Getting a Certificate
Using Letโs Encrypt (Free, Automated)
# Install certbot
sudo apt install certbot
# Get certificate (standalone mode)
sudo certbot certonly --standalone -d example.com -d www.example.com
# Certificate files:
# /etc/letsencrypt/live/example.com/fullchain.pem (cert + intermediates)
# /etc/letsencrypt/live/example.com/privkey.pem (private key)
# Auto-renewal (runs twice daily)
sudo certbot renewUsing ACME Protocol (Programmatic)
# Conceptual example using acme library
from acme import client, messages
from cryptography.hazmat.primitives.asymmetric import ec
# Generate account key
account_key = ec.generate_private_key(ec.SECP256R1())
# Create ACME client
acme_client = client.ClientV2(
directory='https://acme-v02.api.letsencrypt.org/directory',
key=account_key
)
# Request certificate for domain
order = acme_client.new_order(['example.com'])
# Complete challenges (prove domain ownership)
for auth in order.authorizations:
challenge = get_http_challenge(auth)
# Deploy challenge response
# ...
acme_client.answer_challenge(challenge)
# Finalize and get certificate
certificate = acme_client.finalize_order(order, csr)7. Certificate Validation
What Browsers Check
1. Signature chain is valid
โโ Each certificate signed by its issuer
2. Root CA is trusted
โโ In browser/OS trust store
3. Certificate is not expired
โโ Current time within validity period
4. Certificate is not revoked
โโ Check CRL or OCSP
5. Domain matches
โโ Subject or SAN contains the domain
6. Key usage is appropriate
โโ TLS Web Server Authentication
7. Certificate policies are met
โโ Various X.509 constraintsValidation Code Example
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.x509.oid import ExtensionOID
import ssl
import socket
import datetime
def validate_server_certificate(hostname, port=443):
"""Validate a server's certificate"""
# Get certificate
context = ssl.create_default_context()
with socket.create_connection((hostname, port)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
der_cert = ssock.getpeercert(binary_form=True)
cert = x509.load_der_x509_certificate(der_cert, default_backend())
# Check validity period
now = datetime.datetime.utcnow()
if now < cert.not_valid_before or now > cert.not_valid_after:
return False, "Certificate expired or not yet valid"
# Check subject alternative names
try:
san = cert.extensions.get_extension_for_oid(
ExtensionOID.SUBJECT_ALTERNATIVE_NAME
)
names = san.value.get_values_for_type(x509.DNSName)
if hostname not in names:
return False, f"Hostname {hostname} not in SAN"
except x509.ExtensionNotFound:
return False, "No SAN extension"
return True, "Certificate valid"
# Check
valid, message = validate_server_certificate("www.google.com")
print(f"Valid: {valid}, Message: {message}")8. Certificate Revocation
Why Revocation Matters
Scenarios requiring revocation:
- Private key compromised
- Certificate issued incorrectly
- Domain ownership changed
- Organization no longer trusted
Without revocation checking:
- Attacker with stolen key can impersonate site
- Until certificate naturally expires (up to 398 days!)CRL vs OCSP
CRL (Certificate Revocation List):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Big list of revoked serials โ
โ Downloaded periodically โ
โ Can be megabytes โ
โ Delay between updates โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
OCSP (Online Certificate Status Protocol):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Query: "Is this cert revoked?" โ
โ Response: "Yes/No" โ
โ Real-time โ
โ Privacy concerns (CA sees) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
OCSP Stapling:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Server fetches OCSP response โ
โ Staples to TLS handshake โ
โ Client gets fresh proof โ
โ No privacy leak to CA โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ9. Common Certificate Problems
Certificate Errors and Solutions
Error: NET::ERR_CERT_DATE_INVALID
Cause: Certificate expired or not yet valid
Fix: Renew certificate, check server clock
Error: NET::ERR_CERT_AUTHORITY_INVALID
Cause: CA not trusted
Fix: Use well-known CA, or install root cert
Error: NET::ERR_CERT_COMMON_NAME_INVALID
Cause: Domain not in certificate
Fix: Get certificate with correct SAN
Error: SSL_ERROR_BAD_CERT_DOMAIN
Cause: Hostname mismatch
Fix: Access using correct hostname
Error: Certificate chain incomplete
Cause: Missing intermediate certificates
Fix: Configure server with full chainDebugging Certificates
# View certificate details
openssl s_client -connect example.com:443 -showcerts
# Check certificate dates
openssl s_client -connect example.com:443 2>/dev/null | \
openssl x509 -noout -dates
# Check certificate chain
openssl s_client -connect example.com:443 -showcerts 2>/dev/null | \
openssl x509 -noout -issuer -subject
# Verify certificate
openssl verify -CAfile ca-bundle.crt certificate.pem10. Client Certificates
What Theyโre For
Server certificates: Server proves identity to client
Client certificates: Client proves identity to server
Use cases:
- Mutual TLS (mTLS) for service-to-service auth
- User authentication without passwords
- IoT device authentication
- VPN authenticationSetting Up mTLS
import ssl
import socket
# Server side
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
server_context.load_cert_chain('server.crt', 'server.key')
server_context.load_verify_locations('client-ca.crt')
server_context.verify_mode = ssl.CERT_REQUIRED # Require client cert
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind(('0.0.0.0', 8443))
sock.listen()
with server_context.wrap_socket(sock, server_side=True) as ssock:
conn, addr = ssock.accept()
# conn.getpeercert() returns client certificate
# Client side
client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
client_context.load_cert_chain('client.crt', 'client.key')
client_context.load_verify_locations('server-ca.crt')
with socket.create_connection(('server.example.com', 8443)) as sock:
with client_context.wrap_socket(sock, server_hostname='server.example.com') as ssock:
# Connected with mutual authentication
pass11. Self-Signed Certificates
When to Use
Appropriate:
- Development and testing
- Internal services (with private CA)
- Learning and experimentation
Not appropriate:
- Public-facing websites
- Production APIs used by external clients
- Anywhere trust mattersCreating a Self-Signed Certificate
# Generate private key
openssl genrsa -out server.key 2048
# Generate self-signed certificate
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=localhost"
# With SAN extension (required by modern browsers)
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"12. Certificate Transparency
The Problem It Solves
What if a CA issues a certificate for your domain?
- You might never know
- Attacker could intercept traffic
- CA compromise is catastrophic
Certificate Transparency:
- All certificates logged to public logs
- You can monitor logs for your domains
- Misissued certificates are detectableHow It Works
1. CA issues certificate
2. CA submits to CT logs
3. Log returns Signed Certificate Timestamp (SCT)
4. Certificate includes SCT
5. Browsers verify SCT present
6. Domain owners monitor logs
Tools:
- crt.sh (search CT logs)
- Google Certificate Transparency
- Cert Spotter (monitoring)13. Summary
Three things to remember:
Certificates bind keys to identities. A certificate is a signed statement from a CA that a public key belongs to a specific domain or entity.
Trust flows from root CAs. Your browser trusts ~100 root CAs. Those CAs sign intermediates, which sign end certificates. If any link is broken, trust fails.
Certificate management is operational. Renewals, revocations, and chain configuration are ongoing work. Automate with Letโs Encrypt where possible.
14. Whatโs Next
Weโve covered asymmetric cryptography, signatures, and certificates. But thereโs another fundamental primitive: ensuring data hasnโt been modified without proving who modified it.
In the next article: HMAC and Data Integrityโhow to detect tampering with symmetric keys, when you already share a secret.
