Cryptographic Fundamentals
Key Pair (Public Key + Private Key)
The foundation of TLS. Generated together mathematically.
# Generate key pair
openssl genrsa -out private.key 2048
openssl rsa -in private.key -pubout -out public.key
What they do:
- Encrypt with public key → Only private key can decrypt
- Sign with private key → Anyone with public key can verify
Certificate (X.509)
A certificate = public key + identity information + signature from CA.
┌─────────────────────────────────────────────────────────────────┐
│ Certificate Contents │
│ │
│ Subject: CN=api.example.com, O=MyCompany ← Who this is │
│ Issuer: CN=DigiCert CA ← Who signed it │
│ Valid From: 2026-01-01 │
│ Valid To: 2027-01-01 │
│ Public Key: MIIBIjANBgkqhkiG9w0BAQEFA... ← Embedded │
│ Signature: a7f3b2c1d4e5f6... ← CA's stamp │
└─────────────────────────────────────────────────────────────────┘
Certificate is NOT secret - it contains public key and can be shared freely.
Certificate Authority (CA)
An entity with its own key pair, whose job is to sign other certificates.
┌─────────────────────────────────────────────────────────────────┐
│ Root CA Resources │
│ │
│ CA Private Key (HIGHLY SECRET) │
│ - Used to sign certificates │
│ - Stored in HSM (Hardware Security Module) │
│ │
│ CA Certificate (PUBLIC) │
│ - Contains CA's public key │
│ - Self-signed (Issuer = Subject for root CA) │
│ - Distributed to everyone who needs to trust this CA │
└─────────────────────────────────────────────────────────────────┘
AWS Private CA
┌─────────────────────────────────────────────────────────────────┐
│ AWS Private CA provides: │
│ │
│ CA Private Key │
│ - Stored in AWS-managed HSM │
│ - You NEVER see this │
│ - AWS uses it to sign certs on your behalf │
│ │
│ CA Certificate │
│ - You CAN download this │
│ - Put this in API Gateway truststore for mTLS │
│ │
│ Service: Issue certificates signed by this CA │
│ - You submit CSR (Certificate Signing Request) │
│ - AWS signs it with CA private key │
│ - You get back signed certificate │
└─────────────────────────────────────────────────────────────────┘
Client Certificate Creation Flow
Step 1: Client generates key pair
┌─────────────────┐
│ Client │
│ private.key │ ← Keep secret
│ public.key │
└─────────────────┘
Step 2: Client creates CSR (Certificate Signing Request)
┌─────────────────────────────────────────────────────────────────┐
│ CSR contains: │
│ - Client's public key │
│ - Identity info (CN=client.example.com) │
│ - Signed with client's private key (proves ownership) │
└─────────────────────────────────────────────────────────────────┘
Step 3: CA signs the CSR
┌─────────────────┐ CSR ┌─────────────────┐
│ Client │ ───────────────────► │ CA │
│ │ ◄─────────────────── │ Signs with │
│ │ Certificate │ CA private key │
└─────────────────┘ └─────────────────┘
Step 4: Client now has
┌─────────────────┐
│ private.key │ ← Still secret
│ client.crt │ ← Certificate (public key + CA signature)
└─────────────────┘
Why TLS is Needed
Without TLS, attackers can impersonate servers.
Attack Scenarios Without TLS
DNS Spoofing:
You type: bank.com
Attacker poisons DNS
DNS returns: 185.99.99.99 (attacker's server)
You connect to attacker thinking it's bank.com
Man-in-the-Middle (MITM):
┌─────────┐ ┌──────────┐ ┌────────┐
│ You │ ── request ─►│ Attacker │ ── request ─►│ Server │
│ │ ◄─ response ─│ │ ◄─ response ─│ │
└─────────┘ └──────────┘ └────────┘
Attacker sees: passwords, credit cards, session cookies
Attacker can: modify responses, inject malware
Why Attackers Can’t Impersonate WITH TLS
To impersonate bank.com, attacker needs certificate that:
- Has CN/SAN = “bank.com”
- Is signed by a CA in client’s truststore
Option A: Self-signed certificate
- Client checks: Is issuer in my truststore? → NO → CONNECTION REFUSED
Option B: Get real CA to sign
- CA requires proof of domain ownership
- Attacker can’t prove ownership → Request denied
Option C: Steal certificate
- Certificate without private key is useless
- Can’t complete TLS handshake without private key
Standard TLS Flow (Server Certificate Only)
Before TLS:
Server has: private key + certificate (signed by public CA)
Client has: truststore with public CA root certificates (pre-installed)
Client Server
│ │
│ ──────────────────────────────────────────────► │
│ 1. ClientHello │
│ - TLS version, cipher suites, client_random │
│ - SNI: api.example.com │
│ │
│ ◄────────────────────────────────────────────── │
│ 2. ServerHello │
│ - Chosen TLS version, cipher suite │
│ - server_random │
│ │
│ ◄────────────────────────────────────────────── │
│ 3. Server Certificate │
│ - Server's certificate + chain │
│ │
│ ◄────────────────────────────────────────────── │
│ 4. ServerHelloDone │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Client validates server certificate: │ │
│ │ 1. Check not expired │ │
│ │ 2. Check CN/SAN matches hostname │ │
│ │ 3. Build chain to root CA │ │
│ │ 4. Verify root CA in truststore │ │
│ │ 5. Verify signatures in chain │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ──────────────────────────────────────────────► │
│ 5. ClientKeyExchange │
│ - Pre-master secret (encrypted with │
│ server's public key) │
│ │
│ ──────────────────────────────────────────────► │
│ 6. ChangeCipherSpec + Finished │
│ │
│ ◄────────────────────────────────────────────── │
│ 7. ChangeCipherSpec + Finished │
│ │
│ ═══════════════════════════════════════════════ │
│ TLS Session Established │
│ All traffic now encrypted │
│ ═══════════════════════════════════════════════ │
Certificate Chain Validation
Server sends:
┌─────────────────────────────────────────────────────────────────┐
│ Server Certificate │
│ Subject: CN=api.example.com │
│ Issuer: CN=DigiCert SHA2 Secure Server CA │
└─────────────────────────────────────────────────────────────────┘
│
▼ verify with issuer's public key
┌─────────────────────────────────────────────────────────────────┐
│ Intermediate Certificate │
│ Subject: CN=DigiCert SHA2 Secure Server CA │
│ Issuer: CN=DigiCert Global Root CA │
└─────────────────────────────────────────────────────────────────┘
│
▼ verify with root's public key
┌─────────────────────────────────────────────────────────────────┐
│ Root Certificate (in client's truststore) │
│ Subject: CN=DigiCert Global Root CA │
│ Issuer: CN=DigiCert Global Root CA ← Self-signed │
└─────────────────────────────────────────────────────────────────┘
Session Key Derivation
Both sides have:
- client_random
- server_random
- pre_master_secret (client generated, server decrypted)
Both compute:
master_secret = PRF(pre_master_secret, client_random, server_random)
session_keys = PRF(master_secret, ...)
Result: Identical symmetric keys without sending them over network
What Standard TLS Proves
| What | How |
|---|---|
| Server is who it claims | Certificate signed by trusted CA |
| Server owns certificate | Decrypts pre-master secret with private key |
| Traffic encrypted | Symmetric keys from handshake |
| Traffic not tampered | MAC on every message |
Does NOT prove: Who the client is (any client can connect).
mTLS Flow (Mutual TLS)
Both sides prove identity. Used for service-to-service, B2B APIs, IoT.
Before mTLS:
Server has: private key + certificate + truststore (client CAs)
Client has: private key + certificate + truststore (server CAs)
Client Server
│ │
│ ──────────────────────────────────────────────► │
│ 1. ClientHello │
│ │
│ ◄────────────────────────────────────────────── │
│ 2. ServerHello │
│ │
│ ◄────────────────────────────────────────────── │
│ 3. Server Certificate │
│ │
│ ◄────────────────────────────────────────────── │
│ 4. CertificateRequest ◄── NEW in mTLS │
│ - "Send me YOUR certificate" │
│ - Acceptable CA list │
│ │
│ ◄────────────────────────────────────────────── │
│ 5. ServerHelloDone │
│ │
│ ──────────────────────────────────────────────► │
│ 6. Client Certificate ◄── NEW in mTLS │
│ - Client's certificate + chain │
│ │
│ ──────────────────────────────────────────────► │
│ 7. ClientKeyExchange │
│ │
│ ──────────────────────────────────────────────► │
│ 8. CertificateVerify ◄── NEW in mTLS │
│ - Signature of handshake messages │
│ - Signed with CLIENT's private key │
│ - PROVES client owns the certificate │
│ │
│ ──────────────────────────────────────────────► │
│ 9. ChangeCipherSpec + Finished │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Server validates client certificate: │ │
│ │ 1. Check not expired │ │
│ │ 2. Build chain to CA in truststore │ │
│ │ 3. Verify CA signature │ │
│ │ 4. Verify CertificateVerify signature │ │
│ │ → Proves client has private key │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ ◄────────────────────────────────────────────── │
│ 10. ChangeCipherSpec + Finished │
│ │
│ ═══════════════════════════════════════════════ │
│ mTLS Session Established │
│ Both sides verified │
│ ═══════════════════════════════════════════════ │
The 3 New Messages in mTLS
CertificateRequest (Server → Client):
- Server says: “I require client authentication”
- Contains acceptable certificate types and trusted CA list
Client Certificate (Client → Server):
- Client sends certificate (public information)
- Not proof of ownership yet
CertificateVerify (Client → Server):
- Client signs handshake hash with private key
- Server verifies with public key from certificate
- If valid → client proved ownership
Why CertificateVerify Proves Ownership
Private key and public key are mathematically linked:
- Only matching private key can produce valid signature
- Public key can verify but CANNOT create signatures
Server: "Here's random challenge"
Client: sign(challenge, private_key) = signature
Server: verify(signature, public_key) → valid?
If valid → client has the private key → identity confirmed
mTLS Failure Scenarios
Client cert signed by unknown CA:
Client cert issuer: "SomeOther CA"
Truststore contains: "MyCompany CA"
Result: TLS handshake fails - "unknown CA"
Client cert expired:
Valid To: 2025-12-31
Current: 2026-01-02
Result: TLS handshake fails - "certificate expired"
Client doesn’t have private key (stolen cert):
Attacker has certificate but not private key
CertificateVerify: Can't produce valid signature
Result: TLS handshake fails - "bad certificate"
mTLS in API Gateway
Configuration
| Component | What to specify |
|---|---|
| Custom Domain | Required - mTLS only works with custom domains |
| Truststore | S3 URI to PEM file with trusted CA certificates |
| Client Certificate | Certificate signed by CA in truststore |
| Client Private Key | Corresponding private key (stays on client) |
Truststore Contents
Can contain root CA or intermediate CA - whichever signed the client certs.
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQsFAMDC
... (Your CA certificate)
-----END CERTIFICATE-----
curl with mTLS
curl https://api.example.com/data \
--cert client.crt \ # Client certificate (sent to server)
--key client.key \ # Private key (used locally, NOT sent)
--cacert ca-bundle.crt # CA certs to verify server
Important: The private key is NOT sent over the network. curl uses it locally to sign the CertificateVerify message. Only the signature is sent.
TLS vs mTLS Summary
| Aspect | Standard TLS | mTLS |
|---|---|---|
| Server proves identity | ✓ | ✓ |
| Client proves identity | ✗ | ✓ |
| Client needs certificate | No | Yes |
| Client needs private key | No | Yes |
| Server needs truststore | No | Yes |
| Use case | Public websites | Service-to-service, B2B, IoT |
What TLS Protects Against
| Attack | Without TLS | With TLS |
|---|---|---|
| DNS spoofing | Attacker impersonates server | Fake cert rejected |
| MITM on network | Traffic visible/modifiable | Encrypted |
| BGP hijacking | Traffic goes to attacker | Can’t complete handshake |
| Eavesdropping | Plaintext readable | Encrypted |
| Traffic modification | Can inject/modify | MAC detects tampering |