TLS & PKI
Interactive Cryptography Visualizer
Explore symmetric encryption, asymmetric encryption, and the TLS handshake step by step. See how keys are exchanged and data is encrypted in real time.
TLS (Transport Layer Security) is the protocol that secures virtually all internet communication. Every time you see the padlock icon in your browser, TLS is at work — encrypting data in transit, authenticating the server, and ensuring integrity of the connection. PKI (Public Key Infrastructure) is the trust framework that makes TLS possible by binding public keys to verified identities through digital certificates.
Why TLS Matters
Without TLS, all data sent between a client and server travels in plaintext — readable by anyone on the network path. This includes:
- Usernames and passwords
- Credit card numbers and banking details
- Private messages and emails
- API keys and session tokens
- Personal health information
Without TLS:┌────────┐ ┌────────┐│ Client │──── GET /login?pass=secret ────────▶│ Server │└────────┘ ▲ └────────┘ │ ┌─────┴─────┐ │ Attacker │ │ reads: │ │ pass=secret│ └───────────┘
With TLS:┌────────┐ ┌────────┐│ Client │──── 7g$kL9!xmP#2bNq... ──────────▶│ Server │└────────┘ ▲ └────────┘ │ ┌─────┴─────┐ │ Attacker │ │ sees only │ │ encrypted │ │ gibberish │ └───────────┘TLS Provides Three Guarantees
| Guarantee | Description | Cryptographic Mechanism |
|---|---|---|
| Confidentiality | Data cannot be read by eavesdroppers | Symmetric encryption (AES-GCM, ChaCha20) |
| Integrity | Data cannot be modified in transit | MAC (message authentication code) |
| Authentication | Server (and optionally client) identity is verified | Digital certificates + signatures |
The TLS 1.3 Handshake
TLS 1.3 (finalized in 2018) is a major improvement over TLS 1.2. It is faster, simpler, and more secure — completing the handshake in just one round trip (1-RTT) compared to two round trips in TLS 1.2.
TLS 1.3 Handshake (1-RTT):
Client Server────── ──────
│ │ │ ClientHello │ │ - Supported cipher suites │ │ - Key share (ECDHE public key) ──────────▶│ │ - Supported TLS versions │ │ │ │ │ │ ServerHello │ │ - Chosen cipher suite │ │ - Key share (ECDHE) │ │ ◀────────────────────── - Certificate │ │ - CertificateVerify │ │ - Finished │ │ │ │ ┌─────────────────────────────────────────┐ │ │ │ Client now has all it needs: │ │ │ │ 1. Server's ECDHE public key │ │ │ │ 2. Server's certificate (identity) │ │ │ │ 3. Server's signature (proof of identity│) │ │ │ 4. Derives shared symmetric keys │ │ │ └─────────────────────────────────────────┘ │ │ │ │ Finished │ │ (Client confirms handshake) ─────────▶│ │ │ │ ══════════ Encrypted application data ═══════════│ │ ◀════════════════════════════════════════════════▶│ │ │TLS 1.3 vs TLS 1.2
| Feature | TLS 1.2 | TLS 1.3 |
|---|---|---|
| Handshake round trips | 2-RTT | 1-RTT (0-RTT for resumption) |
| Key exchange | RSA or ECDHE | ECDHE only (forward secrecy always) |
| Cipher suites | Many (including weak ones) | Only 5 strong suites |
| Compression | Supported (CRIME attack vector) | Removed |
| Renegotiation | Supported (complex, attack-prone) | Removed |
| 0-RTT resumption | No | Yes (with replay caveats) |
| Static RSA | Supported (no forward secrecy) | Removed |
TLS 1.3 Cipher Suites
TLS 1.3 supports only five cipher suites, all of which provide authenticated encryption:
| Cipher Suite | Key Exchange | Encryption | Hash |
|---|---|---|---|
TLS_AES_256_GCM_SHA384 | ECDHE | AES-256-GCM | SHA-384 |
TLS_AES_128_GCM_SHA256 | ECDHE | AES-128-GCM | SHA-256 |
TLS_CHACHA20_POLY1305_SHA256 | ECDHE | ChaCha20-Poly1305 | SHA-256 |
TLS_AES_128_CCM_SHA256 | ECDHE | AES-128-CCM | SHA-256 |
TLS_AES_128_CCM_8_SHA256 | ECDHE | AES-128-CCM-8 | SHA-256 |
X.509 Certificates
A certificate is a digitally signed document that binds a public key to an identity (a domain name, organization, or person). Certificates are the foundation of trust in TLS.
┌─────────────────────────────────────────────────────────┐│ X.509 Certificate ││ ││ Subject: CN=www.example.com ││ Issuer: CN=Let's Encrypt Authority X3 ││ Valid From: 2024-01-01 00:00:00 UTC ││ Valid To: 2024-03-31 23:59:59 UTC ││ Public Key: EC (P-256) 04:a1:b2:c3:d4:... ││ Serial Number: 03:f7:a1:... ││ ││ Extensions: ││ Subject Alternative Names (SAN): ││ DNS: www.example.com ││ DNS: example.com ││ DNS: api.example.com ││ ││ Signature Algorithm: ECDSA with SHA-256 ││ Signature: 30:45:02:21:00:a1:... ││ (signed by the issuer's private key) │└─────────────────────────────────────────────────────────┘Certificate Fields
| Field | Description |
|---|---|
| Subject | The entity the certificate identifies (e.g., CN=example.com) |
| Issuer | The Certificate Authority that signed the certificate |
| Validity period | Start and end dates for the certificate |
| Public key | The subject’s public key |
| Subject Alternative Names (SAN) | Additional domain names covered by the certificate |
| Serial number | Unique identifier assigned by the CA |
| Signature | The CA’s digital signature proving the certificate is authentic |
Certificate Authorities and the Chain of Trust
A Certificate Authority (CA) is a trusted third party that verifies identities and issues certificates. Trust is established through a chain of trust from the server certificate up to a trusted root CA.
┌─────────────────────────────────────────────────────────┐│ Chain of Trust ││ ││ ┌────────────────┐ ││ │ Root CA │ Pre-installed in OS/browser ││ │ (Self-signed) │ (e.g., DigiCert, ISRG Root X1) ││ └───────┬────────┘ ││ │ Signs ││ ▼ ││ ┌────────────────┐ ││ │ Intermediate CA │ Signed by the Root CA ││ │ │ (e.g., Let's Encrypt R3) ││ └───────┬────────┘ ││ │ Signs ││ ▼ ││ ┌────────────────┐ ││ │ Server Cert │ Signed by the Intermediate CA ││ │ (Leaf cert) │ (e.g., www.example.com) ││ └────────────────┘ ││ ││ Verification: Browser walks the chain from leaf to root││ and verifies each signature. If the root is in the ││ browser's trust store, the entire chain is trusted. │└─────────────────────────────────────────────────────────┘How Certificate Verification Works
- Server presents its certificate chain during the TLS handshake
- Client verifies the leaf certificate’s signature using the intermediate CA’s public key
- Client verifies the intermediate CA’s signature using the root CA’s public key
- Client checks that the root CA is in its trust store (pre-installed list of trusted roots)
- Client checks that the certificate is not expired and has not been revoked
- Client checks that the domain name matches the certificate’s Subject or SAN
Certificate Types
| Type | Validation | What CA Verifies | Use Case |
|---|---|---|---|
| DV (Domain Validation) | Domain ownership only | Automated DNS/HTTP challenge | Blogs, personal sites, APIs |
| OV (Organization Validation) | Domain + organization | Legal entity verification | Business websites |
| EV (Extended Validation) | Domain + org + thorough vetting | Physical address, legal standing | Banks, e-commerce (less common now) |
| Wildcard | Domain + all subdomains | Same as DV/OV | *.example.com |
Let’s Encrypt
Let’s Encrypt is a free, automated, and open Certificate Authority that has revolutionized HTTPS adoption. Before Let’s Encrypt (launched 2015), obtaining a certificate required manual processes and annual fees. Now, certificates can be issued and renewed automatically in seconds.
How ACME Protocol Works
The ACME (Automatic Certificate Management Environment) protocol automates certificate issuance:
┌──────────┐ ┌───────────────┐│ Client │ │ Let's Encrypt ││ (certbot) │ │ ACME Server │└────┬─────┘ └───────┬───────┘ │ │ │ 1. Request certificate │ │ for example.com ─────────▶│ │ │ │ 2. Challenge: prove you │ │ control example.com ◀────────│ │ │ │ 3. Place challenge token at │ │ http://example.com/ │ │ .well-known/acme-challenge/ │ │ (or DNS TXT record) │ │ ─────────▶│ │ │ │ 4. CA verifies the challenge │ │ │ │ 5. Certificate issued ◀────────│ │ │Setting Up Let’s Encrypt
# Install certbotsudo apt-get install certbot python3-certbot-nginx
# Obtain and install certificate (Nginx)sudo certbot --nginx -d example.com -d www.example.com
# Obtain certificate only (manual)sudo certbot certonly --standalone -d example.com
# Auto-renewal (certbot sets up a systemd timer)sudo certbot renew --dry-run
# Certificate files are stored at:# /etc/letsencrypt/live/example.com/fullchain.pem (cert + chain)# /etc/letsencrypt/live/example.com/privkey.pem (private key)server { listen 443 ssl http2; server_name example.com www.example.com;
# Certificate files from Let's Encrypt ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# TLS configuration (strong defaults) ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_prefer_server_ciphers off;
# HSTS (tell browsers to always use HTTPS) add_header Strict-Transport-Security "max-age=63072000" always;
# OCSP stapling (faster certificate verification) ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
location / { proxy_pass http://localhost:3000; }}
# Redirect HTTP to HTTPSserver { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri;}mTLS (Mutual TLS)
Standard TLS authenticates only the server — the client verifies the server’s certificate, but the server does not verify the client. mTLS (mutual TLS) adds client certificate authentication, where both sides present and verify certificates.
Standard TLS: Client ──────────── verifies server cert ──────────▶ Server (anonymous) (authenticated)
mTLS (Mutual TLS): Client ◀─────────── verifies client cert ────────── Server (authenticated) verifies server cert (authenticated) Both sides present certificates. Both sides are verified.When to Use mTLS
| Use Case | Why mTLS |
|---|---|
| Service-to-service communication | Authenticate microservices without API keys |
| Zero-trust networks | Every connection is authenticated, even internal ones |
| IoT device authentication | Devices present client certs instead of passwords |
| API access for partners | More secure than API keys for B2B integrations |
| Kubernetes pod communication | Service mesh (Istio, Linkerd) uses mTLS by default |
mTLS Configuration
import requests
# mTLS: client presents its own certificate to the serverresponse = requests.get( "https://api.internal.example.com/data", cert=( "/path/to/client.crt", # Client certificate "/path/to/client.key", # Client private key ), verify="/path/to/ca-bundle.crt", # CA to verify server cert timeout=30,)print(f"Status: {response.status_code}")print(f"Response: {response.json()}")const https = require("https");const fs = require("fs");
// mTLS: client presents its own certificateconst options = { hostname: "api.internal.example.com", port: 443, path: "/data", method: "GET", // Client certificate and key cert: fs.readFileSync("/path/to/client.crt"), key: fs.readFileSync("/path/to/client.key"), // CA to verify server certificate ca: fs.readFileSync("/path/to/ca-bundle.crt"),};
const req = https.request(options, (res) => { let data = ""; res.on("data", (chunk) => (data += chunk)); res.on("end", () => console.log("Response:", JSON.parse(data)));});req.end();server { listen 443 ssl; server_name api.internal.example.com;
# Server certificate ssl_certificate /etc/ssl/server.crt; ssl_certificate_key /etc/ssl/server.key;
# mTLS: require client certificates ssl_client_certificate /etc/ssl/ca.crt; # CA that signed client certs ssl_verify_client on; # Require valid client cert
# Optional: verify full chain depth ssl_verify_depth 2;
location / { # Pass client certificate info to the application proxy_set_header X-Client-CN $ssl_client_s_dn_cn; proxy_set_header X-Client-Verify $ssl_client_verify; proxy_pass http://localhost:3000; }}Certificate Pinning
Certificate pinning restricts which certificates a client will accept for a particular server, even if the certificate is technically valid and signed by a trusted CA. This protects against compromised CAs and man-in-the-middle attacks using fraudulent certificates.
Without pinning: Any certificate signed by ANY trusted CA is accepted. If attacker compromises ANY CA, they can forge a certificate.
With pinning: Client only accepts certificates matching a specific pin. Even a valid certificate from a different CA is rejected.
┌────────┐ Connects to server ┌────────┐│ Client │─────────────────────────▶│ Server ││ │ │ ││ Pinned:│ Cert presented: │ ││ Pin=X │ Pin=X → ACCEPT │ Cert X ││ │ Pin=Y → REJECT │ │└────────┘ (even if Y is valid) └────────┘Pinning Strategies
| Strategy | What Is Pinned | Pros | Cons |
|---|---|---|---|
| Pin the leaf cert | The exact server certificate | Most restrictive | Must update pin on every cert rotation |
| Pin the intermediate CA | The issuing CA’s certificate | Survives cert rotation | Tied to one CA |
| Pin the public key | The server’s public key (SPKI hash) | Survives cert reissuance if key is reused | Key rotation requires pin update |
HPKP vs Modern Alternatives
| Mechanism | Status | Use Case |
|---|---|---|
| HPKP | Deprecated (removed from browsers) | N/A |
| Certificate Transparency | Active | Publicly log all issued certificates for audit |
| CAA DNS records | Active | Restrict which CAs can issue certificates for your domain |
| App-level pinning | Active | Mobile apps pin server certificates in code |
| DANE/TLSA | Limited adoption | DNS-based certificate association |
CAA DNS Record Example
; Only Let's Encrypt and DigiCert can issue certs for example.comexample.com. CAA 0 issue "letsencrypt.org"example.com. CAA 0 issue "digicert.com"example.com. CAA 0 iodef "mailto:security@example.com"Certificate Revocation
When a private key is compromised or a certificate needs to be invalidated before its expiry date, it must be revoked. There are two main mechanisms:
| Mechanism | How It Works | Pros | Cons |
|---|---|---|---|
| CRL (Certificate Revocation List) | CA publishes a list of revoked serial numbers | Simple | Lists can grow very large; clients must download periodically |
| OCSP (Online Certificate Status Protocol) | Client queries CA in real time for certificate status | Current status | Extra latency; privacy concern (CA sees which sites you visit) |
| OCSP Stapling | Server periodically fetches its own OCSP response and attaches it to the TLS handshake | No extra latency; no privacy leak | Server must be configured correctly |
TLS Configuration Best Practices
| Practice | Recommendation |
|---|---|
| Minimum TLS version | TLS 1.2 (prefer TLS 1.3) |
| Disable | SSLv2, SSLv3, TLS 1.0, TLS 1.1 |
| Forward secrecy | Use ECDHE key exchange exclusively |
| Certificate type | ECDSA P-256 (faster) or RSA-2048+ |
| HSTS | Enable with max-age=63072000 (2 years) |
| OCSP stapling | Enable for faster certificate verification |
| CAA records | Restrict which CAs can issue for your domain |
| Certificate Transparency | Ensure your CA logs all certificates |
| Automated renewal | Use certbot or ACME client to prevent expiration |
| Test your configuration | Use SSL Labs (ssllabs.com) to grade your setup |
SSL Labs Grading
| Grade | Meaning |
|---|---|
| A+ | Excellent — TLS 1.2+, strong ciphers, HSTS, no vulnerabilities |
| A | Good — strong configuration with minor improvements possible |
| B | Acceptable — some weak ciphers or missing HSTS |
| C-F | Poor — weak protocols, vulnerable to known attacks |
Inspecting Certificates
# View a server's certificateopenssl s_client -connect example.com:443 -servername example.com \ < /dev/null 2>/dev/null | openssl x509 -noout -text
# Check certificate expiry dateopenssl s_client -connect example.com:443 -servername example.com \ < /dev/null 2>/dev/null | openssl x509 -noout -dates
# View the full certificate chainopenssl s_client -connect example.com:443 -servername example.com \ -showcerts < /dev/null
# Verify a certificate against a CA bundleopenssl verify -CAfile /etc/ssl/certs/ca-certificates.crt \ server.crt
# Generate a self-signed certificate (for development only)openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout key.pem -out cert.pem -days 365 -nodes \ -subj "/CN=localhost"import sslimport socketfrom datetime import datetime
def inspect_certificate(hostname: str, port: int = 443) -> dict: """Fetch and inspect a server's TLS certificate.""" context = ssl.create_default_context() with socket.create_connection((hostname, port)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as tls: cert = tls.getpeercert()
# Parse key fields subject = dict(x[0] for x in cert["subject"]) issuer = dict(x[0] for x in cert["issuer"]) not_after = datetime.strptime( cert["notAfter"], "%b %d %H:%M:%S %Y %Z" ) days_remaining = (not_after - datetime.utcnow()).days san = [ entry[1] for entry in cert.get("subjectAltName", []) ]
return { "subject": subject.get("commonName"), "issuer": issuer.get("organizationName"), "expires": not_after.isoformat(), "days_remaining": days_remaining, "san": san, "serial": cert.get("serialNumber"), }
# Usageinfo = inspect_certificate("www.google.com")for key, value in info.items(): print(f" {key}: {value}")Quick Reference
| Concept | Description |
|---|---|
| TLS | Protocol for encrypted, authenticated communication |
| TLS 1.3 | Latest version — 1-RTT handshake, mandatory forward secrecy |
| X.509 | Standard format for digital certificates |
| CA | Trusted entity that issues and signs certificates |
| Chain of trust | Leaf cert signed by intermediate, signed by root |
| ACME | Protocol for automated certificate issuance (Let’s Encrypt) |
| mTLS | Both client and server present certificates |
| HSTS | HTTP header forcing browsers to use HTTPS |
| OCSP stapling | Server provides certificate revocation status in handshake |
| CAA | DNS record restricting which CAs can issue for a domain |
| Certificate pinning | Client restricts accepted certificates beyond CA trust |
| Forward secrecy | Ephemeral keys ensure past sessions stay secure if long-term key leaks |