TLS/SSL & Public Key Infrastructure
Why This Matters
Every time you visit a website, check your email, or push code to a repository, your data travels across networks controlled by strangers. Without encryption, anyone sitting between you and the server -- an ISP, a coffee shop Wi-Fi operator, a compromised router -- can read and modify everything in transit. Your passwords, credit card numbers, private messages, all in plain text.
TLS (Transport Layer Security) is the protocol that prevents this. That padlock icon in your browser exists because of TLS, and behind TLS is an entire trust system called Public Key Infrastructure (PKI). Understanding how these pieces fit together is not optional for anyone who operates servers. Misconfigure TLS and you either break your service or leave it exposed. This chapter gives you the conceptual foundation; the next chapter puts it into practice with OpenSSL.
Try This Right Now
See TLS in action on your own machine:
# Connect to a website and see the TLS handshake
openssl s_client -connect example.com:443 -brief
# See the certificate chain your browser would verify
echo | openssl s_client -connect google.com:443 -showcerts 2>/dev/null \
| grep "s:" | head -5
# Check which TLS version a server supports
openssl s_client -connect example.com:443 -tls1_3 -brief 2>&1 | head -3
# View the certificate details
echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
You just performed a TLS handshake, inspected a certificate chain, and checked expiration dates -- all from the command line.
Symmetric vs Asymmetric Encryption
Before we can understand TLS, we need to understand two fundamentally different approaches to encryption.
Symmetric Encryption: One Key, Two Purposes
With symmetric encryption, the same key encrypts and decrypts. Think of it like a physical lock where both parties have a copy of the same key.
Alice Bob
| |
| Plaintext: "Hello Bob" |
| | |
| [Encrypt with shared key K] |
| | |
| Ciphertext: "x7$mQ2..." |
| -------- send over network --------> |
| |
| [Decrypt with same key K]
| |
| Plaintext: "Hello Bob"
Algorithms: AES-256, ChaCha20
Strengths: Extremely fast. AES-256 can encrypt gigabytes per second on modern hardware.
Weakness: How do Alice and Bob agree on the shared key in the first place? If they send it over the network, anyone watching can intercept it. This is called the key distribution problem.
Asymmetric Encryption: Two Keys, Complementary
Asymmetric encryption uses a pair of mathematically related keys: a public key and a private key. What one key encrypts, only the other can decrypt.
Alice Bob
| |
| Bob's Public Key (known to all) |
| <----- Bob publishes it ------------ |
| |
| Plaintext: "Hello Bob" |
| | |
| [Encrypt with Bob's PUBLIC key] |
| | |
| Ciphertext: "j9#kL..." |
| -------- send over network --------> |
| |
| [Decrypt with Bob's PRIVATE key]
| |
| Plaintext: "Hello Bob" |
| |
| Only Bob can decrypt -- only he has |
| the private key. |
Algorithms: RSA, ECDSA, Ed25519
Strengths: Solves the key distribution problem. The public key can be shared openly; only the private key must be kept secret.
Weakness: Much slower than symmetric encryption (100 to 1000 times slower).
The Hybrid Approach (What TLS Actually Does)
TLS uses both. Asymmetric encryption is used briefly at the start to securely exchange a symmetric key. Then the actual data flows using fast symmetric encryption.
Phase 1: Key Exchange (Asymmetric - slow, but only a few messages)
+-------------------------------------------------------------+
| Client and server use asymmetric crypto to agree on a |
| shared secret. No eavesdropper can learn the secret even |
| if they record every byte on the wire. |
+-------------------------------------------------------------+
|
v
Phase 2: Data Transfer (Symmetric - fast, bulk encryption)
+-------------------------------------------------------------+
| All application data (HTTP requests, responses, etc.) is |
| encrypted with the shared symmetric key (AES or ChaCha20). |
+-------------------------------------------------------------+
Think About It: Why not just use asymmetric encryption for everything? Consider a busy web server handling 10,000 requests per second. What would happen to its CPU if every byte of every response used RSA encryption?
How TLS Works: The Handshake
The TLS handshake is the process by which client and server establish a secure connection. Here is the TLS 1.3 handshake, which is the current standard:
Client (Browser) Server (Web Server)
| |
| 1. ClientHello |
| - Supported TLS versions |
| - Supported cipher suites |
| - Client random |
| - Key share (DH public value) |
| -------------------------------------> |
| |
| 2. ServerHello |
| - Chosen TLS version |
| - Chosen cipher suite |
| - Server random |
| - Key share (DH public) |
| |
| 3. EncryptedExtensions |
| 4. Certificate |
| 5. CertificateVerify |
| 6. Finished |
| <------------------------------------- |
| |
| At this point, both sides can |
| compute the shared secret from |
| the key shares (Diffie-Hellman). |
| |
| 7. Finished |
| -------------------------------------> |
| |
| === Encrypted Application Data === |
| <-----------------------------------> |
| |
Let's break down what happens at each step:
-
ClientHello: The client says "Here is what I support" and sends its half of the Diffie-Hellman key exchange.
-
ServerHello: The server picks the strongest mutually-supported cipher suite and sends its half of the key exchange.
-
Certificate: The server sends its certificate so the client can verify it is who it claims to be.
-
CertificateVerify: The server proves it owns the private key matching the certificate by signing the handshake transcript.
-
Finished: Both sides confirm the handshake is complete and unmodified.
After step 2, both sides can compute the shared secret using Diffie-Hellman. From step 3 onward, everything is encrypted.
TLS 1.3 vs TLS 1.2: TLS 1.3 completes the handshake in 1 round trip (1-RTT) instead of 2. It also removed support for insecure algorithms and simplified the protocol. TLS 1.2 is still widely used, but TLS 1.3 is the target for new deployments.
Certificates and X.509
A certificate is a digitally signed document that binds a public key to an identity (like a domain name). The standard format is X.509.
What Is Inside a Certificate?
+--------------------------------------------------+
| X.509 Certificate |
+--------------------------------------------------+
| Version: 3 |
| Serial Number: 04:A3:7B:... |
| Signature Algorithm: SHA256withRSA |
| |
| Issuer: CN=Let's Encrypt Authority X3 |
| O=Let's Encrypt |
| C=US |
| |
| Validity: |
| Not Before: Jan 1 00:00:00 2026 UTC |
| Not After: Apr 1 00:00:00 2026 UTC |
| |
| Subject: CN=www.example.com |
| |
| Subject Public Key Info: |
| Algorithm: RSA (2048-bit) |
| Public Key: 30:82:01:0a:02:82:01:01:... |
| |
| Extensions: |
| Subject Alternative Name (SAN): |
| DNS: www.example.com |
| DNS: example.com |
| Key Usage: Digital Signature, Key Encipherment|
| Basic Constraints: CA:FALSE |
| |
| Signature: a4:b7:c3:d1:... |
| (Signed by the Issuer's private key) |
+--------------------------------------------------+
Key fields:
- Subject -- Who this certificate is for (the domain name).
- Issuer -- Who signed this certificate (the Certificate Authority).
- Validity -- When the certificate is valid. Expired certificates are rejected.
- Public Key -- The subject's public key. Used by clients to encrypt data.
- Subject Alternative Name (SAN) -- Additional domain names the certificate covers. Modern certificates use SAN instead of (or in addition to) the CN.
- Signature -- The issuer's digital signature proving the certificate has not been tampered with.
Certificate Chains and Trust
A single certificate is not enough. Browsers and operating systems need to trust it. This is where certificate chains come in.
The Chain of Trust
+---------------------------+
| Root CA Certificate | Stored in browser/OS trust store
| (Self-signed) | Offline, heavily guarded
| Validity: 20+ years |
+---------------------------+
|
| Signs
v
+---------------------------+
| Intermediate CA Cert | Operated by the CA
| Issued by Root CA | Used for day-to-day signing
| Validity: 5-10 years |
+---------------------------+
|
| Signs
v
+---------------------------+
| Leaf Certificate | Your server's certificate
| (End-entity cert) | The one you install on Nginx
| Issued by Intermediate |
| Validity: 90 days-1 yr |
+---------------------------+
When your browser connects to www.example.com:
- The server sends its leaf certificate and the intermediate certificate.
- The browser checks: "Was this leaf cert signed by this intermediate?"
- The browser checks: "Was this intermediate signed by a root CA I trust?"
- The browser has a built-in list of trusted root CAs. If the chain leads to one of them, the connection is trusted.
Why Intermediates?
The root CA's private key is incredibly valuable. If it were compromised, every certificate it ever signed would be suspect. By keeping the root offline (literally in a vault), and using intermediates for daily signing, the root is protected. If an intermediate is compromised, only that intermediate is revoked -- not the root.
# See the root CAs your system trusts
ls /etc/ssl/certs/ | head -20
# Count how many root CAs your system trusts
ls /etc/ssl/certs/ | wc -l
# On Debian/Ubuntu, the bundle file is:
cat /etc/ssl/certs/ca-certificates.crt | grep "BEGIN CERTIFICATE" | wc -l
Distro Note: The CA certificate bundle lives in different places:
- Debian/Ubuntu:
/etc/ssl/certs/ca-certificates.crt- RHEL/Fedora:
/etc/pki/tls/certs/ca-bundle.crt- Both are managed by the
ca-certificatespackage.
Think About It: What happens if you visit a website whose certificate was signed by a root CA that your browser does not trust? You have seen this -- it is the "Your connection is not private" warning. What would an attacker need to do to make a fake certificate that your browser trusts?
Public and Private Keys
The public key and private key are mathematically related but computationally infeasible to derive one from the other.
How They Work Together
SIGNING (proves identity):
+----------------------------------------------------+
| 1. Server hashes the message |
| 2. Server encrypts the hash with its PRIVATE key |
| This encrypted hash is the "signature" |
| 3. Client decrypts signature with PUBLIC key |
| 4. Client hashes the message independently |
| 5. If hashes match --> signature is valid |
+----------------------------------------------------+
ENCRYPTION (protects data):
+----------------------------------------------------+
| 1. Client encrypts data with server's PUBLIC key |
| 2. Only the server's PRIVATE key can decrypt it |
+----------------------------------------------------+
The private key must never leave the server. If someone obtains your private key, they can:
- Impersonate your server
- Decrypt recorded traffic (unless you use forward secrecy)
- Sign things as you
# A private key file should always have strict permissions
ls -l /etc/ssl/private/
# Expected: drwx--x--- root ssl-cert
ls -l /etc/ssl/private/server.key
# Expected: -rw-r----- root ssl-cert
# Or even: -rw------- root root
Key Types
| Algorithm | Key Size | Security Level | Speed | Usage |
|---|---|---|---|---|
| RSA | 2048-bit | Good | Slower | Legacy, widely supported |
| RSA | 4096-bit | Better | Slow | High-security needs |
| ECDSA | 256-bit | Good (= RSA 3072) | Fast | Modern, recommended |
| Ed25519 | 256-bit | Good | Fastest | SSH keys, newer TLS |
ECDSA with P-256 offers equivalent security to RSA 3072-bit at a fraction of the computational cost. For new deployments, ECDSA is the standard choice.
The CSR: Certificate Signing Request
When you want a Certificate Authority to issue you a certificate, you do not send them your private key (never do that). Instead, you create a CSR.
What Is in a CSR?
+-----------------------------------------------+
| Certificate Signing Request |
+-----------------------------------------------+
| Subject: |
| CN = www.example.com |
| O = Example Corp |
| C = IN |
| |
| Public Key: (your public key) |
| |
| Signature: (signed with YOUR private key, |
| proving you own the corresponding key) |
+-----------------------------------------------+
The flow:
You (Server Admin) Certificate Authority
| |
1. Generate key pair |
(private key + public key) |
| |
2. Create CSR |
(embed public key + identity) |
| |
3. Send CSR -----------------------> |
| |
| 4. Verify you control the domain
| (via HTTP challenge, DNS, etc.)
| |
| 5. Sign the certificate
| (using CA's private key)
| |
6. Receive signed cert <------------- |
| |
7. Install cert + private key |
on your web server |
The private key never leaves your server. The CA never sees it. The CSR proves you own the matching private key without revealing it.
Why PKI Matters
Without PKI, the internet has no way to answer the question: "Am I really talking
to bank.example.com, or is someone pretending to be them?"
PKI provides three guarantees:
- Authentication -- The server is who it claims to be (verified by the CA chain).
- Confidentiality -- The data is encrypted in transit (symmetric encryption with keys exchanged via asymmetric crypto).
- Integrity -- The data has not been tampered with (message authentication codes).
What Happens Without TLS
Without TLS (HTTP):
+--------+ +----------+ +--------+
| Client | --- "password" --> | Attacker | --- "password" --> | Server |
+--------+ (plaintext) +----------+ (plaintext) +--------+
|
Attacker reads
your password!
With TLS (HTTPS):
+--------+ +----------+ +--------+
| Client | --- "x7#kQ..." --> | Attacker | --- "x7#kQ..." --> | Server |
+--------+ (encrypted) +----------+ (encrypted) +--------+
|
Attacker sees only
encrypted garbage.
Certificate Formats
Certificates come in several formats. Knowing which is which will save you hours of frustration.
PEM (Privacy Enhanced Mail)
The most common format on Linux. Base64-encoded with header/footer markers.
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJALKhBz3h7qGhMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAklOMQ0wCwYDVQQIDARUZXN0MQ0wCwYDVQQHDARUZXN0MRgwFgYDVQQKDA9F
... (base64 data) ...
eG1wbGUgQ29ycDAeFw0yNjAxMDEwMDAwMDBaFw0yNjA0MDEwMDAwMDBaMAAwggEi
-----END CERTIFICATE-----
- File extensions:
.pem,.crt,.cer,.key - Used by: Apache, Nginx, most Linux tools
- You can concatenate multiple PEM certificates into one file (certificate chain).
DER (Distinguished Encoding Rules)
Binary format. Not human-readable.
- File extensions:
.der,.cer - Used by: Java, Windows, some embedded systems
- Contains the same data as PEM, just binary-encoded instead of Base64.
PKCS#12 / PFX
A binary format that bundles the private key, certificate, and chain together in one password-protected file.
- File extensions:
.p12,.pfx - Used by: Windows, Java keystores, importing/exporting key+cert pairs
- Useful for transferring credentials between systems.
Converting Between Formats
# PEM to DER
openssl x509 -in cert.pem -outform DER -out cert.der
# DER to PEM
openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem
# PEM cert + key to PKCS#12
openssl pkcs12 -export -out bundle.p12 \
-inkey private.key -in cert.pem -certfile chain.pem
# PKCS#12 to PEM
openssl pkcs12 -in bundle.p12 -out everything.pem -nodes
We will cover these commands in detail in the next chapter (OpenSSL Hands-On).
Quick Reference
+------------+--------+----------+-----------+------------------------+
| Format | Encode | Readable | Extension | Common Use |
+------------+--------+----------+-----------+------------------------+
| PEM | Base64 | Yes | .pem .crt | Linux, Nginx, Apache |
| DER | Binary | No | .der .cer | Java, Windows |
| PKCS#12 | Binary | No | .p12 .pfx | Import/Export bundles |
+------------+--------+----------+-----------+------------------------+
Hands-On: Inspect a Real Certificate Chain
Let's examine the certificate chain for a real website:
# Connect and show the full certificate chain
echo | openssl s_client -connect www.google.com:443 -showcerts 2>/dev/null
In the output, you will see multiple -----BEGIN CERTIFICATE----- blocks. These
are the certificates in the chain, from leaf to intermediate.
# Extract just the subject and issuer of each cert in the chain
echo | openssl s_client -connect www.google.com:443 -showcerts 2>/dev/null \
| awk '/BEGIN CERT/,/END CERT/{print}' \
| csplit -z -f cert- - '/BEGIN CERT/' '{*}' 2>/dev/null
# Now examine each certificate
for f in cert-*; do
echo "=== $f ==="
openssl x509 -in "$f" -noout -subject -issuer
echo ""
done
# Clean up
rm -f cert-*
You should see something like:
=== cert-00 ===
subject=CN = www.google.com
issuer=C = US, O = Google Trust Services, CN = GTS CA 1C3
=== cert-01 ===
subject=C = US, O = Google Trust Services, CN = GTS CA 1C3
issuer=C = US, O = Google Trust Services LLC, CN = GTS Root R1
Notice how each certificate's issuer matches the next certificate's subject. That is the chain of trust.
Debug This
A colleague configures TLS on a new server, but clients see "certificate verify failed" errors. They run:
$ echo | openssl s_client -connect newserver.example.com:443 2>&1 | head -20
And see:
depth=0 CN = newserver.example.com
verify error:num=20:unable to get local issuer certificate
verify error:num=21:unable to verify the first certificate
Verify return code: 21 (unable to verify the first certificate)
The certificate itself is valid and not expired. What is the problem?
Answer: The server is sending only the leaf certificate, not the intermediate certificate. The client cannot build the chain from the leaf to a trusted root CA. The fix is to configure the server to send the full chain:
For Nginx:
ssl_certificate /etc/ssl/certs/fullchain.pem; # leaf + intermediate
ssl_certificate_key /etc/ssl/private/server.key;
The fullchain.pem file should contain the leaf certificate followed by the
intermediate certificate(s), concatenated together.
What Just Happened?
+------------------------------------------------------------------+
| TLS/SSL & PUBLIC KEY INFRASTRUCTURE |
+------------------------------------------------------------------+
| |
| ENCRYPTION TYPES: |
| Symmetric - Same key encrypts/decrypts (AES) - fast |
| Asymmetric - Key pair: public + private (RSA/ECDSA) - slow |
| TLS uses both: asymmetric for key exchange, symmetric for |
| data transfer (hybrid approach). |
| |
| TLS HANDSHAKE (1.3): |
| ClientHello --> ServerHello + Cert --> Finished |
| 1 round trip, then encrypted data flows. |
| |
| CERTIFICATES (X.509): |
| Bind a public key to a domain name. |
| Fields: Subject, Issuer, Validity, Public Key, SAN. |
| |
| CHAIN OF TRUST: |
| Root CA --> Intermediate CA --> Leaf (your cert) |
| Browsers trust root CAs; intermediates bridge the gap. |
| |
| CSR FLOW: |
| Generate key pair --> Create CSR --> Send to CA |
| --> CA verifies domain --> CA signs cert --> You install it |
| |
| FORMATS: |
| PEM (Base64, Linux) | DER (Binary, Java) | PKCS#12 (Bundle) |
| |
+------------------------------------------------------------------+
Try This
Exercise 1: Inspect Certificates
Pick five websites you use daily. For each one, use openssl s_client to determine:
- What TLS version is used?
- What cipher suite is negotiated?
- Who issued the certificate?
- When does it expire?
- How many certificates are in the chain?
# Template command
echo | openssl s_client -connect DOMAIN:443 -brief 2>&1
echo | openssl s_client -connect DOMAIN:443 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
Exercise 2: Trace the Trust
For one of those certificates, trace the full chain. Find the root CA in your system's trust store:
# Find the root CA name from the chain
echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null \
| grep "s:.*CN"
# Search for it in the system trust store
grep -r "ISRG Root" /etc/ssl/certs/ 2>/dev/null | head -3
Exercise 3: Expired Certificate
Use openssl s_client to connect to expired.badssl.com:443. Read the error
messages. What exactly does the output tell you about why the connection is
untrusted?
Bonus Challenge
Draw your own ASCII diagram of what happens when you type https://example.com in
your browser, from DNS resolution through the TLS handshake to the first encrypted
HTTP request. Include every step, every key exchange, and every verification. This
exercise forces you to truly understand the entire flow.