Project 4: Mutual TLS (mTLS) Mesh - Identity at the Wire

Project 4: Mutual TLS (mTLS) Mesh - Identity at the Wire

Build a complete mTLS infrastructure where services authenticate each other cryptographically, eliminating network-based trust entirely.

Quick Reference

Attribute Value
Difficulty Level 3: Advanced
Time Estimate 1 Week
Language Go (Alternatives: Python, Rust)
Prerequisites Basic TLS understanding, Go programming, command-line proficiency
Key Topics PKI, X.509 Certificates, Certificate Authorities, TLS Handshake, SPIFFE
Knowledge Area Cryptography / Distributed Systems
Tools OpenSSL, PKI concepts, SPIFFE
Main Book โ€œZero Trust Networksโ€ by Gilman & Barth

1. Learning Objectives

By completing this project, you will:

  1. Master PKI fundamentals: Build and operate your own Certificate Authority from scratch
  2. Understand X.509 certificates deeply: Know every field and its security implications
  3. Implement mutual authentication: Both client proves identity to server AND server proves identity to client
  4. Automate certificate lifecycle: Issue, rotate, and revoke certificates programmatically
  5. Design for Zero Trust: Eliminate network-based trust through cryptographic identity
  6. Debug TLS issues: Diagnose certificate chain problems, SAN mismatches, and expiry issues
  7. Apply SPIFFE concepts: Understand workload identity in modern cloud-native environments

2. Theoretical Foundation

2.1 Why mTLS is the Gold Standard for Zero Trust

In traditional TLS (what you use when visiting https://example.com), only the server proves its identity. The client verifies โ€œIโ€™m talking to the real example.comโ€ but the server has no cryptographic proof of who the client is.

Mutual TLS (mTLS) adds client certificates, creating bidirectional authentication:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Standard TLS vs Mutual TLS                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚  STANDARD TLS (One-Way):                                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”‚
โ”‚  โ”‚ Client โ”‚ โ”€โ”€โ”€โ”€ "Who are you?" โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ Server โ”‚                      โ”‚
โ”‚  โ”‚        โ”‚ โ—€โ”€โ”€โ”€ Server Certificate โ”€โ”€โ”€โ”€ โ”‚        โ”‚                      โ”‚
โ”‚  โ”‚        โ”‚      (Proves server ID)      โ”‚        โ”‚                      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ”‚
โ”‚      โœ“ Client knows server is legitimate                                 โ”‚
โ”‚      โœ— Server has NO IDEA who client is                                  โ”‚
โ”‚                                                                          โ”‚
โ”‚  MUTUAL TLS (Two-Way):                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”‚
โ”‚  โ”‚ Client โ”‚ โ”€โ”€โ”€โ”€ "Who are you?" โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚ Server โ”‚                      โ”‚
โ”‚  โ”‚        โ”‚ โ—€โ”€โ”€โ”€ Server Certificate โ”€โ”€โ”€โ”€ โ”‚        โ”‚                      โ”‚
โ”‚  โ”‚        โ”‚ โ”€โ”€โ”€โ”€ Client Certificate โ”€โ”€โ”€โ–ถ โ”‚        โ”‚                      โ”‚
โ”‚  โ”‚        โ”‚      (Proves client ID)      โ”‚        โ”‚                      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ”‚
โ”‚      โœ“ Client knows server is legitimate                                 โ”‚
โ”‚      โœ“ Server knows client is legitimate                                 โ”‚
โ”‚      โœ“ Both identities are CRYPTOGRAPHICALLY proven                      โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Why this matters for Zero Trust:

  • An attacker on the network cannot impersonate either party
  • Traffic cannot be sniffed (encrypted with session keys derived from handshake)
  • No reliance on network position - identity is in the certificate, not the IP address
  • Every connection is authenticated, regardless of source network

2.2 Public Key Infrastructure (PKI) Fundamentals

PKI is the framework that makes certificate-based authentication possible. At its core, it solves the key distribution problem: How do you securely share public keys with entities youโ€™ve never met?

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         PKI Trust Hierarchy                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚                       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                            โ”‚
โ”‚                       โ”‚     Root CA         โ”‚                            โ”‚
โ”‚                       โ”‚  (Self-Signed)      โ”‚                            โ”‚
โ”‚                       โ”‚  HIGHLY PROTECTED   โ”‚                            โ”‚
โ”‚                       โ”‚  (Offline/HSM)      โ”‚                            โ”‚
โ”‚                       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                            โ”‚
โ”‚                                  โ”‚                                       โ”‚
โ”‚                         Signs certificates                               โ”‚
โ”‚                                  โ”‚                                       โ”‚
โ”‚           โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”               โ”‚
โ”‚           โ”‚                      โ”‚                      โ”‚               โ”‚
โ”‚           โ–ผ                      โ–ผ                      โ–ผ               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚
โ”‚  โ”‚ Intermediate CA โ”‚   โ”‚ Intermediate CA โ”‚   โ”‚ Intermediate CA โ”‚        โ”‚
โ”‚  โ”‚   (Services)    โ”‚   โ”‚   (Users)       โ”‚   โ”‚   (Devices)     โ”‚        โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚
โ”‚           โ”‚                     โ”‚                     โ”‚                  โ”‚
โ”‚           โ–ผ                     โ–ผ                     โ–ผ                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚
โ”‚  โ”‚ payment-service โ”‚   โ”‚ alice@corp.com  โ”‚   โ”‚ laptop-001      โ”‚        โ”‚
โ”‚  โ”‚   certificate   โ”‚   โ”‚   certificate   โ”‚   โ”‚   certificate   โ”‚        โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚
โ”‚                                                                          โ”‚
โ”‚  Certificate Chain for payment-service:                                  โ”‚
โ”‚  [payment-service cert] โ†’ [Intermediate CA] โ†’ [Root CA]                  โ”‚
โ”‚                                                                          โ”‚
โ”‚  Verification: "I trust Root CA. Root CA signed Intermediate.            โ”‚
โ”‚                 Intermediate signed payment-service. Therefore,          โ”‚
โ”‚                 I trust payment-service."                                โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Key PKI Concepts:

Concept Definition Security Implication
Root CA The ultimate trust anchor, self-signed Compromise = entire PKI compromised
Intermediate CA Signed by Root, signs end-entity certs Limits blast radius if compromised
End-Entity Cert The actual service/user certificate Whatโ€™s presented during handshake
Certificate Chain Path from end-entity to trusted root Must be complete and valid
Trust Store Collection of trusted root certificates What CA certs your system trusts

2.3 X.509 Certificate Structure

X.509 is the standard format for public key certificates. Understanding its structure is essential for debugging TLS issues:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     X.509 Certificate Structure                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ TBSCertificate (To Be Signed - the actual certificate data)       โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚                                                                    โ”‚  โ”‚
โ”‚  โ”‚  Version:              3 (v3)                                      โ”‚  โ”‚
โ”‚  โ”‚  Serial Number:        Unique ID (e.g., 17:32:A4:...)              โ”‚  โ”‚
โ”‚  โ”‚  Signature Algorithm:  sha256WithRSAEncryption                     โ”‚  โ”‚
โ”‚  โ”‚                                                                    โ”‚  โ”‚
โ”‚  โ”‚  Issuer:               CN=My Root CA, O=MyOrg                      โ”‚  โ”‚
โ”‚  โ”‚                        (WHO signed this cert)                      โ”‚  โ”‚
โ”‚  โ”‚                                                                    โ”‚  โ”‚
โ”‚  โ”‚  Validity:                                                         โ”‚  โ”‚
โ”‚  โ”‚    Not Before:         2024-01-01 00:00:00 UTC                     โ”‚  โ”‚
โ”‚  โ”‚    Not After:          2024-01-02 00:00:00 UTC  โ† SHORT-LIVED!     โ”‚  โ”‚
โ”‚  โ”‚                                                                    โ”‚  โ”‚
โ”‚  โ”‚  Subject:              CN=payment-service                          โ”‚  โ”‚
โ”‚  โ”‚                        (WHO this cert identifies)                  โ”‚  โ”‚
โ”‚  โ”‚                                                                    โ”‚  โ”‚
โ”‚  โ”‚  Subject Public Key:   <RSA 2048-bit public key>                   โ”‚  โ”‚
โ”‚  โ”‚                                                                    โ”‚  โ”‚
โ”‚  โ”‚  Extensions (X.509 v3):                                            โ”‚  โ”‚
โ”‚  โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚  โ”‚
โ”‚  โ”‚    โ”‚ Subject Alternative Name (SAN):      โ† CRITICAL FOR mTLS    โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”‚   DNS: payment-service.prod.local                           โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”‚   DNS: payment.internal                                     โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”‚   URI: spiffe://trust-domain/ns/prod/sa/payment             โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”‚   IP:  10.0.1.50                                            โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”คโ”‚  โ”‚
โ”‚  โ”‚    โ”‚ Key Usage:                                                  โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”‚   digitalSignature, keyEncipherment                         โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”คโ”‚  โ”‚
โ”‚  โ”‚    โ”‚ Extended Key Usage:                                         โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”‚   TLS Web Server Authentication (for server certs)          โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”‚   TLS Web Client Authentication (for client certs)          โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”คโ”‚  โ”‚
โ”‚  โ”‚    โ”‚ Basic Constraints:                                          โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ”‚   CA: FALSE  (this is an end-entity, not a CA)              โ”‚โ”‚  โ”‚
โ”‚  โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚  โ”‚
โ”‚  โ”‚                                                                    โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                                          โ”‚
โ”‚  Signature Algorithm:  sha256WithRSAEncryption                           โ”‚
โ”‚  Signature:            <CA's signature over TBSCertificate>              โ”‚
โ”‚                        (This proves the CA issued this cert)             โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Critical Fields for mTLS:

Field Purpose Common Issues
Subject Alternative Name (SAN) Lists all valid identities for this cert Connection fails if hostname not in SAN
Extended Key Usage Restricts what the cert can be used for Client auth requires clientAuth EKU
Validity Period When cert is valid Short-lived certs (hours) reduce breach impact
Issuer Who signed this cert Must chain to trusted CA

2.4 Certificate Signing Request (CSR)

A CSR is how an entity requests a certificate from a CA. It contains the public key and requested identity, signed by the corresponding private key (proving ownership):

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    CSR Generation and Signing Flow                       โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚  Step 1: Service Generates Key Pair                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚ payment-service                                                  โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚   Private Key: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ  (NEVER leaves service)         โ”‚    โ”‚
โ”‚  โ”‚   Public Key:  ABCD1234...       (Goes in CSR)                  โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                    โ”‚                                     โ”‚
โ”‚  Step 2: Create and Sign CSR       โ”‚                                     โ”‚
โ”‚                                    โ–ผ                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚ Certificate Signing Request (CSR)                                โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚   Subject: CN=payment-service                                    โ”‚    โ”‚
โ”‚  โ”‚   Public Key: ABCD1234...                                        โ”‚    โ”‚
โ”‚  โ”‚   Requested SANs: payment-service.prod.local                     โ”‚    โ”‚
โ”‚  โ”‚   Signature: <signed with private key>  โ† Proves key ownership   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                    โ”‚                                     โ”‚
โ”‚  Step 3: Submit to CA              โ”‚                                     โ”‚
โ”‚                                    โ–ผ                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚ Certificate Authority                                            โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚   1. Verify CSR signature (proves key ownership)                 โ”‚    โ”‚
โ”‚  โ”‚   2. Validate identity (is this really payment-service?)         โ”‚    โ”‚
โ”‚  โ”‚   3. Apply policy (cert duration, allowed SANs, etc.)            โ”‚    โ”‚
โ”‚  โ”‚   4. Sign certificate with CA private key                        โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                    โ”‚                                     โ”‚
โ”‚  Step 4: Return Signed Certificate โ”‚                                     โ”‚
โ”‚                                    โ–ผ                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚ X.509 Certificate                                                โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚   Subject: CN=payment-service                                    โ”‚    โ”‚
โ”‚  โ”‚   Issuer: CN=My CA                                               โ”‚    โ”‚
โ”‚  โ”‚   Valid: 2024-01-01 to 2024-01-02 (24 hours)                     โ”‚    โ”‚
โ”‚  โ”‚   SANs: payment-service.prod.local                               โ”‚    โ”‚
โ”‚  โ”‚   CA Signature: <signed by CA>                                   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2.5 The TLS Handshake: Standard vs Mutual

Understanding the TLS handshake is critical for debugging connection issues:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    TLS 1.3 Handshake (Standard)                          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚    Client                                              Server            โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ClientHello โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚              โ”‚
โ”‚       โ”‚    - Supported cipher suites                      โ”‚              โ”‚
โ”‚       โ”‚    - Supported TLS versions                       โ”‚              โ”‚
โ”‚       โ”‚    - Client random (32 bytes)                     โ”‚              โ”‚
โ”‚       โ”‚    - Key share (for key exchange)                 โ”‚              โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ServerHello โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚              โ”‚
โ”‚       โ”‚    - Chosen cipher suite                          โ”‚              โ”‚
โ”‚       โ”‚    - Server random (32 bytes)                     โ”‚              โ”‚
โ”‚       โ”‚    - Key share (for key exchange)                 โ”‚              โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ EncryptedExtensions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚              โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ Certificate (Server's) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚  โ† Server     โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ CertificateVerify โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚    proves     โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ Finished โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚    identity   โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Finished โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚              โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ—€โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Application Data โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ–ถ โ”‚              โ”‚
โ”‚       โ”‚              (Encrypted with session keys)        โ”‚              โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    TLS 1.3 Handshake (Mutual TLS)                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚    Client                                              Server            โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ClientHello โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚              โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ServerHello โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚              โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ EncryptedExtensions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚              โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ CertificateRequest โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚  โ† Server     โ”‚
โ”‚       โ”‚        (Server asks for client cert)              โ”‚    requests  โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ Certificate (Server's) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚    client     โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ CertificateVerify โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚    auth       โ”‚
โ”‚       โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€ Finished โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚              โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Certificate (Client's) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚  โ† Client    โ”‚
โ”‚       โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ CertificateVerify โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚    proves     โ”‚
โ”‚       โ”‚             (Signed with client's private key)    โ”‚    identity  โ”‚
โ”‚       โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Finished โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ  โ”‚              โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚       โ”‚ โ—€โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Application Data โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ–ถ โ”‚              โ”‚
โ”‚       โ”‚                                                   โ”‚              โ”‚
โ”‚  RESULT: Both parties have cryptographic proof of identity               โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

What Happens at Each Step:

Message Purpose Whatโ€™s Verified
ClientHello Initiate handshake, propose parameters -
ServerHello Accept parameters, provide key share -
CertificateRequest Server asks client to authenticate -
Certificate (Server) Server sends its cert chain Client verifies chain to trusted CA
CertificateVerify (Server) Proves server owns private key Signature over handshake transcript
Certificate (Client) Client sends its cert chain Server verifies chain to trusted CA
CertificateVerify (Client) Proves client owns private key Signature over handshake transcript
Finished Confirms handshake integrity MAC over entire handshake

2.6 SPIFFE: Workload Identity for the Cloud

SPIFFE (Secure Production Identity Framework for Everyone) standardizes workload identity:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         SPIFFE Architecture                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚  SPIFFE ID Format:                                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  spiffe://trust-domain/path/to/workload                         โ”‚    โ”‚
โ”‚  โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                        โ”‚    โ”‚
โ”‚  โ”‚       Trust Domain         Workload Path                        โ”‚    โ”‚
โ”‚  โ”‚       (your org)           (identifies specific workload)       โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  Examples:                                                               โ”‚
โ”‚    spiffe://example.com/ns/production/sa/payment-service                 โ”‚
โ”‚    spiffe://example.com/region/us-west/app/frontend                      โ”‚
โ”‚    spiffe://example.com/cluster/k8s-prod/pod/api-server-abc123           โ”‚
โ”‚                                                                          โ”‚
โ”‚  SVID (SPIFFE Verifiable Identity Document):                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚ X.509 Certificate with SPIFFE ID in SAN:                        โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚   Subject: (can be empty or generic)                             โ”‚    โ”‚
โ”‚  โ”‚   SAN URI: spiffe://example.com/ns/prod/sa/payment               โ”‚    โ”‚
โ”‚  โ”‚   Valid: Short-lived (1 hour typical)                            โ”‚    โ”‚
โ”‚  โ”‚   Issuer: SPIFFE-compliant CA                                    โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚ The SPIFFE ID in the URI SAN IS the identity.                    โ”‚    โ”‚
โ”‚  โ”‚ Traditional Subject field is not used for identity.              โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  SPIRE (SPIFFE Runtime Environment):                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ SPIRE Serverโ”‚โ—€โ”€โ”€ Attestation โ”€โ”€โ”€โ–ถโ”‚ SPIRE Agent โ”‚            โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  (CA + DB)  โ”‚                     โ”‚ (per node)  โ”‚            โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ”‚    โ”‚
โ”‚  โ”‚                                             โ”‚                    โ”‚    โ”‚
โ”‚  โ”‚                               Unix Socket API                    โ”‚    โ”‚
โ”‚  โ”‚                                             โ”‚                    โ”‚    โ”‚
โ”‚  โ”‚                         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚                         โ–ผ                   โ–ผ               โ–ผ   โ”‚    โ”‚
โ”‚  โ”‚                   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚                   โ”‚ Workload โ”‚        โ”‚ Workload โ”‚    โ”‚ Workload โ”‚   โ”‚
โ”‚  โ”‚                   โ”‚    A     โ”‚        โ”‚    B     โ”‚    โ”‚    C     โ”‚   โ”‚
โ”‚  โ”‚                   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Why SPIFFE Matters:

  • Platform-agnostic identity: Works across Kubernetes, VMs, bare metal
  • Automatic rotation: SPIRE handles cert lifecycle
  • Attestation-based: Workloads prove identity through multiple signals
  • Zero Trust native: No implicit trust based on network

2.7 Certificate Revocation: When Things Go Wrong

What happens when a private key is compromised or a certificate needs to be invalidated before expiry?

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Certificate Revocation Methods                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚  METHOD 1: Certificate Revocation Lists (CRLs)                           โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  CA publishes list of revoked certificate serial numbers:        โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ CRL (Certificate Revocation List)                       โ”‚    โ”‚    โ”‚
โ”‚  โ”‚  โ”‚                                                         โ”‚    โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   Issuer: CN=My CA                                      โ”‚    โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   This Update: 2024-01-15 00:00:00                      โ”‚    โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   Next Update: 2024-01-16 00:00:00                      โ”‚    โ”‚    โ”‚
โ”‚  โ”‚  โ”‚                                                         โ”‚    โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   Revoked Certificates:                                 โ”‚    โ”‚    โ”‚
โ”‚  โ”‚  โ”‚     Serial: 1234:5678  Revoked: 2024-01-14  Key Compromise    โ”‚   โ”‚
โ”‚  โ”‚  โ”‚     Serial: ABCD:EF01  Revoked: 2024-01-15  Superseded        โ”‚   โ”‚
โ”‚  โ”‚  โ”‚                                                         โ”‚    โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  Problems:                                                       โ”‚    โ”‚
โ”‚  โ”‚  - CRLs grow large over time (linear growth)                     โ”‚    โ”‚
โ”‚  โ”‚  - Clients must download entire CRL                              โ”‚    โ”‚
โ”‚  โ”‚  - Stale data between updates                                    โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  METHOD 2: Online Certificate Status Protocol (OCSP)                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚    Client              OCSP Responder              CA           โ”‚    โ”‚
โ”‚  โ”‚       โ”‚                      โ”‚                      โ”‚            โ”‚    โ”‚
โ”‚  โ”‚       โ”‚ "Is cert 1234 valid?"โ”‚                      โ”‚            โ”‚    โ”‚
โ”‚  โ”‚       โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ                      โ”‚            โ”‚    โ”‚
โ”‚  โ”‚       โ”‚                      โ”‚ (checks status)      โ”‚            โ”‚    โ”‚
โ”‚  โ”‚       โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                      โ”‚            โ”‚    โ”‚
โ”‚  โ”‚       โ”‚  "Good" / "Revoked"  โ”‚                      โ”‚            โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  Advantages: Real-time, small response size                      โ”‚    โ”‚
โ”‚  โ”‚  Problems: Privacy (CA knows what certs you're checking)         โ”‚    โ”‚
โ”‚  โ”‚            Availability (if OCSP down, what to do?)              โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  METHOD 3: Short-Lived Certificates (Zero Trust Approach)                โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  Issue certificates valid for 1 hour instead of 1 year.          โ”‚    โ”‚
โ”‚  โ”‚  No revocation needed - just don't renew.                        โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  Timeline:                                                       โ”‚    โ”‚
โ”‚  โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค     โ”‚    โ”‚
โ”‚  โ”‚  0        15m        30m        45m        60m                   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚         โ”‚          โ”‚          โ”‚          โ”‚                    โ”‚    โ”‚
โ”‚  โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Cert 1 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค                    โ”‚    โ”‚
โ”‚  โ”‚            โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Cert 2 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค          โ”‚    โ”‚
โ”‚  โ”‚                     (overlap for smooth rotation)                โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  Compromise at 35m? Cert expires at 60m anyway.                  โ”‚    โ”‚
โ”‚  โ”‚  Maximum exposure: remaining validity period.                    โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  This is the SPIFFE/Istio/modern cloud-native approach.          โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2.8 Identity Bootstrapping: The Chicken-and-Egg Problem

How does a new service prove who it is to get its first certificate?

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Identity Bootstrapping Methods                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚  THE PROBLEM:                                                            โ”‚
โ”‚  New service starts โ†’ needs certificate to communicate                   โ”‚
โ”‚  But CA asks: "Prove you are payment-service"                            โ”‚
โ”‚  Service has no cert yet to prove anything!                              โ”‚
โ”‚                                                                          โ”‚
โ”‚  SOLUTION 1: Pre-shared Token/Secret                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  1. Operator pre-provisions a one-time token                     โ”‚    โ”‚
โ”‚  โ”‚  2. Service presents token to CA                                 โ”‚    โ”‚
โ”‚  โ”‚  3. CA verifies token, issues certificate                        โ”‚    โ”‚
โ”‚  โ”‚  4. Token is invalidated                                         โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  Used by: Vault, manual PKI                                      โ”‚    โ”‚
โ”‚  โ”‚  Problem: How do you securely distribute the token?              โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  SOLUTION 2: Platform Attestation (SPIRE approach)                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  Service proves identity by WHERE it's running:                  โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  Kubernetes:  Pod UID, Service Account, Namespace                โ”‚    โ”‚
โ”‚  โ”‚  AWS:         Instance ID, IAM Role, Security Groups            โ”‚    โ”‚
โ”‚  โ”‚  GCP:         Service Account, Project, Zone                     โ”‚    โ”‚
โ”‚  โ”‚  Linux:       Process UID, binary hash, parent process           โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  Workload โ”‚ โ”€โ”€โ”€โ–ถ โ”‚  SPIRE Agent โ”‚ โ”€โ”€โ”€โ–ถ โ”‚ Kubernetes โ”‚        โ”‚    โ”‚
โ”‚  โ”‚  โ”‚           โ”‚      โ”‚              โ”‚      โ”‚    API     โ”‚        โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚    โ”‚
โ”‚  โ”‚       โ”‚                    โ”‚                    โ”‚                โ”‚    โ”‚
โ”‚  โ”‚       โ”‚                    โ”‚     "Pod abc123 in namespace        โ”‚    โ”‚
โ”‚  โ”‚       โ”‚                    โ”‚      prod with SA payment-svc"      โ”‚    โ”‚
โ”‚  โ”‚       โ”‚                    โ”‚                    โ”‚                โ”‚    โ”‚
โ”‚  โ”‚       โ”‚                    โ–ผ                    โ”‚                โ”‚    โ”‚
โ”‚  โ”‚       โ”‚             โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”‚                โ”‚    โ”‚
โ”‚  โ”‚       โ”‚             โ”‚ SPIRE Server โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                โ”‚    โ”‚
โ”‚  โ”‚       โ”‚             โ”‚  (matches to โ”‚                             โ”‚    โ”‚
โ”‚  โ”‚       โ”‚             โ”‚   SPIFFE ID) โ”‚                             โ”‚    โ”‚
โ”‚  โ”‚       โ”‚             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                             โ”‚    โ”‚
โ”‚  โ”‚       โ”‚                    โ”‚                                     โ”‚    โ”‚
โ”‚  โ”‚       โ—€โ”€โ”€โ”€ SVID (cert) โ”€โ”€โ”€โ”€โ”˜                                     โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  SOLUTION 3: Instance Identity Documents (Cloud Provider)                โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  AWS: Instance Identity Document signed by AWS                   โ”‚    โ”‚
โ”‚  โ”‚  GCP: Instance Identity Token from metadata server               โ”‚    โ”‚
โ”‚  โ”‚  Azure: Managed Identity tokens                                  โ”‚    โ”‚
โ”‚  โ”‚                                                                  โ”‚    โ”‚
โ”‚  โ”‚  CA trusts cloud provider's attestation.                         โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

3. Complete Project Specification

3.1 What You Will Build

A complete mTLS mesh system consisting of:

  1. Mini Certificate Authority (CA): Issues and manages short-lived X.509 certificates
  2. Two Services: Server and client that communicate exclusively via mTLS
  3. Automatic Certificate Rotation: Refresh certificates before expiry without connection drops
  4. Certificate Inspection CLI: Debug and verify certificate chains

3.2 Functional Requirements

FR-1: Mini CA

  • Generate and secure a Root CA key pair
  • Accept Certificate Signing Requests (CSRs) via API
  • Issue certificates with configurable validity (default: 1 hour)
  • Include SPIFFE-style URIs in Subject Alternative Names
  • Maintain an in-memory list of issued certificates
  • Support certificate revocation (mark as revoked)

FR-2: Server Service

  • Expose an HTTPS endpoint requiring client certificates
  • Validate client certificates against the CAโ€™s root
  • Extract and log the clientโ€™s identity from the certificate
  • Reject connections from unknown/expired/revoked certificates

FR-3: Client Service

  • Obtain a certificate from the CA at startup
  • Present certificate when connecting to the server
  • Validate the serverโ€™s certificate against the CAโ€™s root
  • Successfully complete an mTLS handshake

FR-4: Certificate Rotation

  • Monitor certificate expiry time
  • Automatically request new certificate before expiry (e.g., at 50% lifetime)
  • Swap certificates without dropping active connections
  • Log rotation events

FR-5: Certificate Inspector CLI

  • Parse and display certificate details
  • Verify certificate chains
  • Check certificate validity (dates, signatures)
  • Display SPIFFE IDs from SAN URIs

3.3 Non-Functional Requirements

  • Security: Private keys never leave their respective components
  • Availability: Certificate rotation must not cause connection failures
  • Observability: All TLS events must be loggable
  • Portability: Run on any system with Go installed

3.4 System Architecture Overview

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         mTLS Mesh Architecture                           โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                        Mini CA                                   โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ Root Key Pair โ”‚  โ”‚   CSR API     โ”‚  โ”‚ Issued Certs DB   โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  (Protected)  โ”‚  โ”‚  /api/sign    โ”‚  โ”‚ (In-Memory)       โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                   โ”‚                                      โ”‚
โ”‚                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”‚
โ”‚                    โ”‚   CA Root Certificate       โ”‚                      โ”‚
โ”‚                    โ”‚   (Distributed to all)      โ”‚                      โ”‚
โ”‚                    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ”‚
โ”‚                                   โ”‚                                      โ”‚
โ”‚         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚         โ”‚                         โ”‚                         โ”‚           โ”‚
โ”‚         โ–ผ                         โ”‚                         โ–ผ           โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  Server Service โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€ mTLS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚  Client Service โ”‚    โ”‚
โ”‚  โ”‚                 โ”‚             โ”‚              โ”‚                 โ”‚    โ”‚
โ”‚  โ”‚ - Server cert   โ”‚             โ”‚              โ”‚ - Client cert   โ”‚    โ”‚
โ”‚  โ”‚ - Server key    โ”‚             โ”‚              โ”‚ - Client key    โ”‚    โ”‚
โ”‚  โ”‚ - CA root (for  โ”‚             โ”‚              โ”‚ - CA root (for  โ”‚    โ”‚
โ”‚  โ”‚   client verify)โ”‚             โ”‚              โ”‚   server verify)โ”‚    โ”‚
โ”‚  โ”‚ - Rotation      โ”‚             โ”‚              โ”‚ - Rotation      โ”‚    โ”‚
โ”‚  โ”‚   goroutine     โ”‚                            โ”‚   goroutine     โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  Data Flow:                                                              โ”‚
โ”‚  1. Service generates key pair locally                                   โ”‚
โ”‚  2. Service creates CSR, sends to CA                                     โ”‚
โ”‚  3. CA validates and signs, returns certificate                          โ”‚
โ”‚  4. Service uses cert+key for mTLS connections                           โ”‚
โ”‚  5. Rotation monitor refreshes cert before expiry                        โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

4. Real World Outcome

When you complete this project, hereโ€™s exactly what youโ€™ll see:

Terminal 1: Start the Mini CA

$ ./mtls-ca --port 8443 --cert-duration 1h

[CA] Generating Root CA key pair (RSA-4096)...
[CA] Root CA certificate created:
     Subject:    CN=mtls-mesh-root-ca
     Serial:     1A:2B:3C:4D:5E:6F:70:81
     Valid:      2024-01-15 00:00:00 UTC to 2034-01-15 00:00:00 UTC
     Key Usage:  Certificate Sign, CRL Sign

[CA] API server listening on :8443
[CA] Endpoints:
     POST /api/v1/sign     - Submit CSR, receive signed certificate
     GET  /api/v1/ca       - Download CA root certificate
     POST /api/v1/revoke   - Revoke a certificate

[CA] Ready to issue certificates.

Terminal 2: Start the Server Service

$ ./mtls-server --ca-url http://localhost:8443 --listen :9443

[SERVER] Generating service key pair (ECDSA P-256)...
[SERVER] Creating CSR for identity: spiffe://mesh.local/service/api-server
[SERVER] Requesting certificate from CA at http://localhost:8443/api/v1/sign...

[CA] (on CA side) Received CSR:
     Subject:     CN=api-server
     Requested SAN: spiffe://mesh.local/service/api-server

[SERVER] Certificate issued:
     Serial:      2B:3C:4D:5E:6F:70:81:92
     Issuer:      CN=mtls-mesh-root-ca
     Subject:     CN=api-server
     SAN URI:     spiffe://mesh.local/service/api-server
     Valid:       2024-01-15 10:00:00 UTC to 2024-01-15 11:00:00 UTC
     Expires in:  59m 59s

[SERVER] Starting mTLS server on :9443
[SERVER] Client certificate required: YES
[SERVER] Trusted CA: CN=mtls-mesh-root-ca

[SERVER] Certificate rotation scheduled:
     Next rotation: 2024-01-15 10:30:00 UTC (30 minutes)

[SERVER] Ready to accept mTLS connections.

Terminal 3: Start the Client Service

$ ./mtls-client --ca-url http://localhost:8443 --server https://localhost:9443

[CLIENT] Generating service key pair (ECDSA P-256)...
[CLIENT] Creating CSR for identity: spiffe://mesh.local/service/payment-worker
[CLIENT] Requesting certificate from CA...

[CLIENT] Certificate issued:
     Serial:      3C:4D:5E:6F:70:81:92:A3
     Subject:     CN=payment-worker
     SAN URI:     spiffe://mesh.local/service/payment-worker
     Valid:       2024-01-15 10:00:05 UTC to 2024-01-15 11:00:05 UTC

[CLIENT] Initiating mTLS connection to localhost:9443...

[CLIENT] TLS Handshake Details:
     Cipher Suite:    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
     TLS Version:     TLS 1.3
     Server Identity: spiffe://mesh.local/service/api-server
     Our Identity:    spiffe://mesh.local/service/payment-worker

[CLIENT] Connection established! Server verified our identity.

[CLIENT] Sending request: GET /api/status
[CLIENT] Response: 200 OK
         Body: {"status":"healthy","caller":"spiffe://mesh.local/service/payment-worker"}

[CLIENT] mTLS mesh communication successful!

Terminal 2 (Server) - Showing Client Connection

[SERVER] New connection from 127.0.0.1:54321
[SERVER] Client certificate presented:
     Subject:     CN=payment-worker
     SAN URI:     spiffe://mesh.local/service/payment-worker
     Issuer:      CN=mtls-mesh-root-ca
     Serial:      3C:4D:5E:6F:70:81:92:A3

[SERVER] Client identity verified: spiffe://mesh.local/service/payment-worker
[SERVER] Handling request: GET /api/status
[SERVER] Response sent to payment-worker: 200 OK

Certificate Inspection with OpenSSL

# View client certificate details
$ openssl x509 -in client.crt -text -noout

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            3c:4d:5e:6f:70:81:92:a3
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN = mtls-mesh-root-ca
        Validity
            Not Before: Jan 15 10:00:05 2024 GMT
            Not After : Jan 15 11:00:05 2024 GMT
        Subject: CN = payment-worker
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:8a:3b:...
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                URI:spiffe://mesh.local/service/payment-worker
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
    Signature Algorithm: ecdsa-with-SHA256
        30:45:02:21:...

Testing with curl

# This succeeds - mTLS with valid client cert
$ curl --cacert ca.crt \
       --cert client.crt \
       --key client.key \
       https://localhost:9443/api/status

{"status":"healthy","caller":"spiffe://mesh.local/service/payment-worker"}

# This fails - no client certificate
$ curl --cacert ca.crt https://localhost:9443/api/status

curl: (56) OpenSSL SSL_read: error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate

# This fails - wrong CA (server doesn't trust this CA)
$ curl --cacert other-ca.crt \
       --cert other-client.crt \
       --key other-client.key \
       https://localhost:9443/api/status

curl: (60) SSL certificate problem: unable to get local issuer certificate

Certificate Rotation in Action

# 30 minutes later...

[SERVER] Certificate rotation triggered (50% lifetime reached)
[SERVER] Generating new CSR...
[SERVER] New certificate issued:
     Serial:      4D:5E:6F:70:81:92:A3:B4
     Valid:       2024-01-15 10:30:00 UTC to 2024-01-15 11:30:00 UTC

[SERVER] Hot-swapping certificate...
[SERVER] Active connections: 3 (will use new cert for new connections)
[SERVER] Certificate rotation complete.
[SERVER] Next rotation: 2024-01-15 11:00:00 UTC

The Core Question Youโ€™re Answering

โ€œHow can services prove their identity to each other cryptographically, eliminating the need to trust the network?โ€

This question strikes at the heart of Zero Trust networking. In traditional architectures, being โ€œinside the networkโ€ grants implicit trust - if a service can reach another service, itโ€™s assumed to be legitimate. mTLS flips this model entirely: every connection must prove identity through cryptographic certificates, regardless of network position.

Your challenge is to build a system where:

  • A service cannot communicate without possessing a valid certificate signed by a trusted authority
  • Both the caller and the callee verify each otherโ€™s identity before exchanging any data
  • Network position (IP address, subnet, VPN) provides zero trust - only the certificate matters
  • Compromised credentials expire automatically through short-lived certificates

Concepts You Must Understand First

Before diving into implementation, ensure you have a solid grasp of these foundational concepts:

1. X.509 Certificates and PKI (Public Key Infrastructure)

What to know:

  • X.509 is the standard format for public key certificates used in TLS
  • A certificate binds a public key to an identity (like โ€œpayment-serviceโ€)
  • The binding is attested by a Certificate Authorityโ€™s signature
  • PKI is the hierarchy of trust: Root CA -> Intermediate CA -> End-Entity Certificates

Key questions to answer:

  • What fields in an X.509 certificate are critical for mTLS?
  • Why do we use intermediate CAs instead of signing everything with the root?
  • What makes a certificate โ€œtrustedโ€?

2. TLS Handshake: Standard vs Mutual

What to know:

  • Standard TLS: Only the server proves its identity (what you see with HTTPS)
  • Mutual TLS: Both client AND server prove identity via certificates
  • The handshake establishes: identity verification, cipher agreement, session key derivation

Key questions to answer:

  • At what point in the handshake does the client send its certificate?
  • What triggers the client to send a certificate (hint: CertificateRequest)?
  • How does each party prove it owns the private key corresponding to its certificate?

3. Certificate Signing Requests (CSR)

What to know:

  • A CSR is how you request a certificate from a CA
  • It contains your public key and requested identity (Subject, SANs)
  • The CSR is signed with your private key, proving you own the key pair
  • The CA validates the CSR and creates a signed certificate

Key questions to answer:

  • Why does the CSR need to be signed by the requestor?
  • What fields from the CSR end up in the final certificate?
  • Can the CA add fields not in the CSR? (Yes - like validity period, extensions)

4. SPIFFE/SPIRE Workload Identity

What to know:

  • SPIFFE provides a standard identity format: spiffe://trust-domain/path
  • SPIFFE IDs are placed in the URI SAN of X.509 certificates (SVIDs)
  • SPIRE is the reference implementation: attestation, identity issuance, rotation

Key questions to answer:

  • Why use SPIFFE IDs instead of traditional Subject (CN) fields?
  • How does a workload prove its identity to get its first certificate?
  • What is โ€œattestationโ€ and why is it necessary?

5. Certificate Rotation and Lifecycle

What to know:

  • Short-lived certificates (hours, not years) reduce breach impact
  • Rotation must happen before expiry to avoid service disruption
  • Atomic certificate swap prevents connection failures during rotation

Key questions to answer:

  • Whatโ€™s a good rotation threshold? (Common: 50% of lifetime)
  • How do active connections handle certificate rotation?
  • What happens if rotation fails? (Retry logic, fallback)

Questions to Guide Your Design

Work through these questions before writing code. Theyโ€™ll shape your architecture:

Certificate Authority Design

  1. Where will the CA private key be stored? In memory? On disk? In an HSM? What are the tradeoffs?
  2. How will you generate serial numbers? Must be unique - what happens if you reuse one?
  3. What certificate validity period will you use? 1 hour? 24 hours? Why?
  4. Will you support certificate revocation? If not, why do short-lived certs make this acceptable?

Identity Model

  1. How will you structure SPIFFE IDs? What path format? (e.g., /ns/namespace/sa/serviceaccount)
  2. How will a service prove it should receive a particular identity? (The bootstrapping problem)
  3. Will you validate requested identities, or trust the requestor?

mTLS Configuration

  1. What TLS version will you require? TLS 1.2 minimum? TLS 1.3 only?
  2. What ClientAuth mode will you use? RequireAndVerifyClientCert is the only truly secure option for mTLS
  3. How will you configure the trust store? RootCAs vs ClientCAs - do you understand the difference?

Rotation Strategy

  1. When will rotation trigger? Fixed schedule? Percentage of lifetime?
  2. How will you atomically swap certificates? What synchronization is needed?
  3. What happens to existing connections during rotation?

Thinking Exercise

Before writing any code, work through this exercise on paper or a whiteboard:

Trace the Complete mTLS Handshake

Draw the message flow between Client and Server for a TLS 1.3 mTLS connection. For each message, note:

  1. ClientHello
    • What does the client send?
    • What capabilities is it advertising?
  2. ServerHello + EncryptedExtensions
    • What has the server decided?
    • Why are extensions encrypted now?
  3. CertificateRequest
    • What is the server asking for?
    • What CAs will the clientโ€™s cert need to chain to?
  4. Certificate (Server) + CertificateVerify
    • Whatโ€™s in the Certificate message?
    • What does CertificateVerify prove? (Not just โ€œI have a certโ€)
  5. Certificate (Client) + CertificateVerify
    • Why is this step necessary for mTLS?
    • What exactly is the client signing?
  6. Finished (both sides)
    • What does Finished prove about the handshake?

Bonus: What would happen if an attacker intercepted all these messages and replayed them later?


Hints in Layers

If you get stuck, reveal hints progressively. Try to solve each phase before looking at hints.

Layer 1: Getting Started

Hint: First step with OpenSSL

Start by generating certificates manually with OpenSSL before writing any Go code. This will help you understand what your CA needs to produce:

# Generate a CA key and self-signed cert
openssl ecparam -name prime256v1 -genkey -noout -out ca.key
openssl req -new -x509 -key ca.key -out ca.crt -days 365 -subj "/CN=My CA"

Once you can generate, sign, and verify certificates manually, translating to Go becomes straightforward.

Layer 2: Goโ€™s crypto/tls Configuration

Hint: The critical tls.Config settings for mTLS

For the server to require client certificates:

tlsConfig := &tls.Config{
    ClientAuth: tls.RequireAndVerifyClientCert,  // This is the key!
    ClientCAs:  caCertPool,                       // CAs that can sign client certs
}

For the client to present its certificate:

tlsConfig := &tls.Config{
    Certificates: []tls.Certificate{clientCert},  // Your cert + key
    RootCAs:      caCertPool,                     // CAs that can sign server certs
}

Common mistake: Using RootCAs on the server for client verification - thatโ€™s wrong! RootCAs is for outgoing connections, ClientCAs is for verifying incoming client certs.

Layer 3: Adding SPIFFE IDs to Certificates

Hint: URI SANs in Go's x509 package

SPIFFE IDs go in the URI Subject Alternative Name. In Go:

import "net/url"

spiffeID, _ := url.Parse("spiffe://mesh.local/service/payment")

template := &x509.Certificate{
    // ... other fields ...
    URIs: []*url.URL{spiffeID},  // This adds the SPIFFE ID as a URI SAN
}

To extract a SPIFFE ID from a certificate:

for _, uri := range cert.URIs {
    if uri.Scheme == "spiffe" {
        fmt.Println("SPIFFE ID:", uri.String())
    }
}

Layer 4: Certificate Rotation

Hint: Using GetCertificate for dynamic certificates

Instead of setting Certificates directly, use a callback:

tlsConfig := &tls.Config{
    GetCertificate: func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
        cm.mu.RLock()
        defer cm.mu.RUnlock()
        return cm.currentCert, nil  // Return the latest cert
    },
}

For client-side rotation, use GetClientCertificate:

tlsConfig := &tls.Config{
    GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
        cm.mu.RLock()
        defer cm.mu.RUnlock()
        return cm.currentCert, nil
    },
}

The rotation goroutine atomically swaps cm.currentCert - new connections automatically use the new cert.

Layer 5: Debugging Certificate Issues

Hint: Common error messages and their causes
Error Likely Cause
certificate signed by unknown authority CA not in trust store (RootCAs or ClientCAs)
bad certificate Client didnโ€™t send cert, or server rejected it
certificate is valid for X, not Y Hostname not in certificateโ€™s SAN
certificate has expired Check NotAfter, also check system clock!
certificate specifies incompatible key usage Missing clientAuth or serverAuth EKU

Debug with OpenSSL:

# Verbose connection test
openssl s_client -connect localhost:9443 \
    -cert client.crt -key client.key \
    -CAfile ca.crt -debug

5. Solution Architecture

5.1 Component Design

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         Component Architecture                           โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                           mini-ca/                               โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   main.go     โ”‚  โ”‚   ca.go       โ”‚  โ”‚   handlers.go     โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚               โ”‚  โ”‚               โ”‚  โ”‚                   โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - CLI flags   โ”‚  โ”‚ - GenerateRootโ”‚  โ”‚ - POST /sign      โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - Start serverโ”‚  โ”‚ - SignCSR     โ”‚  โ”‚ - GET  /ca        โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚               โ”‚  โ”‚ - Track issuedโ”‚  โ”‚ - POST /revoke    โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                          mtls-server/                            โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   main.go     โ”‚  โ”‚  certmgr.go   โ”‚  โ”‚   server.go       โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚               โ”‚  โ”‚               โ”‚  โ”‚                   โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - CLI flags   โ”‚  โ”‚ - GenerateKey โ”‚  โ”‚ - mTLS config     โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - Bootstrap   โ”‚  โ”‚ - CreateCSR   โ”‚  โ”‚ - RequireClientCertโ”‚  โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - Start serverโ”‚  โ”‚ - RequestCert โ”‚  โ”‚ - Identity extractโ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚               โ”‚  โ”‚ - Rotate()    โ”‚  โ”‚                   โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                          mtls-client/                            โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   main.go     โ”‚  โ”‚  certmgr.go   โ”‚  โ”‚   client.go       โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚               โ”‚  โ”‚               โ”‚  โ”‚                   โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - CLI flags   โ”‚  โ”‚ - GenerateKey โ”‚  โ”‚ - mTLS config     โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - Bootstrap   โ”‚  โ”‚ - CreateCSR   โ”‚  โ”‚ - Dial with cert  โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - Make requestโ”‚  โ”‚ - RequestCert โ”‚  โ”‚ - Verify server   โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚               โ”‚  โ”‚ - Rotate()    โ”‚  โ”‚                   โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                           shared/                                โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   spiffe.go   โ”‚  โ”‚   certutil.go โ”‚  โ”‚   x509ext.go      โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚               โ”‚  โ”‚               โ”‚  โ”‚                   โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - ParseID     โ”‚  โ”‚ - PEM encode  โ”‚  โ”‚ - Add SANs        โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - ValidateID  โ”‚  โ”‚ - PEM decode  โ”‚  โ”‚ - Add EKU         โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ”‚ - FormatID    โ”‚  โ”‚ - Chain verifyโ”‚  โ”‚ - Parse exts      โ”‚   โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

5.2 Key Data Structures

// CertificateRequest represents a CSR submission
type CertificateRequest struct {
    CSR         []byte   `json:"csr"`          // PEM-encoded CSR
    RequestedID string   `json:"requested_id"` // SPIFFE ID to include in SAN
    TTL         Duration `json:"ttl"`          // Requested validity duration
}

// CertificateResponse from the CA
type CertificateResponse struct {
    Certificate []byte    `json:"certificate"` // PEM-encoded cert
    Chain       [][]byte  `json:"chain"`       // Intermediate certs (if any)
    ExpiresAt   time.Time `json:"expires_at"`
    Serial      string    `json:"serial"`
}

// IssuedCertificate tracked by the CA
type IssuedCertificate struct {
    Serial    *big.Int
    Subject   string
    SpiffeID  string
    IssuedAt  time.Time
    ExpiresAt time.Time
    Revoked   bool
    RevokedAt time.Time
}

// CertificateManager handles cert lifecycle
type CertificateManager struct {
    mu          sync.RWMutex
    privateKey  crypto.PrivateKey
    certificate *tls.Certificate
    caRoots     *x509.CertPool
    spiffeID    string
    caURL       string
    rotateAt    time.Time
}

5.3 Certificate Rotation Algorithm

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Certificate Rotation Flow                             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                          โ”‚
โ”‚   Certificate Timeline:                                                  โ”‚
โ”‚                                                                          โ”‚
โ”‚   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค    โ”‚
โ”‚   โ”‚ Issue   25%      50%      75%      Expiry                      โ”‚    โ”‚
โ”‚   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผ                           โ”‚    โ”‚
โ”‚   โ”‚         โ”‚        โ”‚        โ”‚        โ”‚                           โ”‚    โ”‚
โ”‚   โ”‚         โ”‚  Rotation Window โ”‚        โ”‚                           โ”‚    โ”‚
โ”‚   โ”‚         โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚        โ”‚                           โ”‚    โ”‚
โ”‚   โ”‚         โ”‚                โ”‚        โ”‚                           โ”‚    โ”‚
โ”‚   โ”‚         โ”‚   Get new cert โ”‚        โ”‚                           โ”‚    โ”‚
โ”‚   โ”‚         โ”‚   before old   โ”‚        โ”‚                           โ”‚    โ”‚
โ”‚   โ”‚         โ”‚   expires      โ”‚        โ”‚                           โ”‚    โ”‚
โ”‚                                                                          โ”‚
โ”‚   Rotation Process:                                                      โ”‚
โ”‚                                                                          โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚   โ”‚  1. Timer fires at 50% of cert lifetime                         โ”‚   โ”‚
โ”‚   โ”‚                                                                  โ”‚   โ”‚
โ”‚   โ”‚  2. Generate new key pair (optional - can reuse key)            โ”‚   โ”‚
โ”‚   โ”‚                                                                  โ”‚   โ”‚
โ”‚   โ”‚  3. Create new CSR with same identity                           โ”‚   โ”‚
โ”‚   โ”‚                                                                  โ”‚   โ”‚
โ”‚   โ”‚  4. Submit to CA, receive new certificate                       โ”‚   โ”‚
โ”‚   โ”‚                                                                  โ”‚   โ”‚
โ”‚   โ”‚  5. Atomically swap certificate in memory:                      โ”‚   โ”‚
โ”‚   โ”‚                                                                  โ”‚   โ”‚
โ”‚   โ”‚     manager.mu.Lock()                                           โ”‚   โ”‚
โ”‚   โ”‚     manager.certificate = newCert                               โ”‚   โ”‚
โ”‚   โ”‚     manager.rotateAt = calculateNextRotation(newCert)           โ”‚   โ”‚
โ”‚   โ”‚     manager.mu.Unlock()                                         โ”‚   โ”‚
โ”‚   โ”‚                                                                  โ”‚   โ”‚
โ”‚   โ”‚  6. Existing connections continue with old cert (fine)          โ”‚   โ”‚
โ”‚   โ”‚     New connections use new cert                                โ”‚   โ”‚
โ”‚   โ”‚                                                                  โ”‚   โ”‚
โ”‚   โ”‚  7. Schedule next rotation                                      โ”‚   โ”‚
โ”‚   โ”‚                                                                  โ”‚   โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

6. Phased Implementation Guide

Phase 1: Generate Root CA with OpenSSL (Day 1)

Before writing any Go code, understand the OpenSSL commands that your CA will replicate:

Task 1.1: Create the Root CA

# Create directory structure
mkdir -p pki/{root,intermediate,certs,private}
chmod 700 pki/private

# Generate Root CA private key (RSA 4096-bit)
openssl genrsa -out pki/private/root-ca.key 4096
chmod 600 pki/private/root-ca.key

# Create Root CA configuration file
cat > pki/root-ca.conf << 'EOF'
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
prompt = no

[req_distinguished_name]
CN = mtls-mesh-root-ca
O = My Organization
C = US

[v3_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:TRUE, pathlen:1
keyUsage = critical, keyCertSign, cRLSign
EOF

# Generate self-signed Root CA certificate (10 years validity)
openssl req -new -x509 -sha256 \
    -key pki/private/root-ca.key \
    -out pki/root/root-ca.crt \
    -days 3650 \
    -config pki/root-ca.conf

# Verify the Root CA certificate
openssl x509 -in pki/root/root-ca.crt -text -noout

Expected Output:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: ... (random)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = mtls-mesh-root-ca, O = My Organization, C = US
        Validity
            Not Before: Jan 15 00:00:00 2024 GMT
            Not After : Jan 15 00:00:00 2034 GMT
        Subject: CN = mtls-mesh-root-ca, O = My Organization, C = US
        ...
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:1
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign

Checkpoint: You have a self-signed Root CA certificate with proper CA extensions.

Phase 2: Sign Server and Client Certificates (Day 2)

Task 2.1: Create Server Certificate

# Generate server private key (ECDSA P-256 for efficiency)
openssl ecparam -name prime256v1 -genkey -noout -out pki/private/server.key

# Create server CSR configuration with SAN
cat > pki/server.conf << 'EOF'
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = api-server

[v3_req]
subjectAltName = @alt_names
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
basicConstraints = CA:FALSE

[alt_names]
URI.1 = spiffe://mesh.local/service/api-server
DNS.1 = localhost
DNS.2 = api-server.mesh.local
IP.1 = 127.0.0.1
EOF

# Generate CSR
openssl req -new -sha256 \
    -key pki/private/server.key \
    -out pki/certs/server.csr \
    -config pki/server.conf

# View the CSR
openssl req -in pki/certs/server.csr -text -noout

Task 2.2: Sign the Server Certificate with the CA

# Create signing configuration (for the CA to use)
cat > pki/sign-server.conf << 'EOF'
[v3_server]
subjectAltName = @alt_names
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
basicConstraints = CA:FALSE

[alt_names]
URI.1 = spiffe://mesh.local/service/api-server
DNS.1 = localhost
DNS.2 = api-server.mesh.local
IP.1 = 127.0.0.1
EOF

# Sign the CSR (1 hour validity for short-lived cert demo)
openssl x509 -req \
    -in pki/certs/server.csr \
    -CA pki/root/root-ca.crt \
    -CAkey pki/private/root-ca.key \
    -CAcreateserial \
    -out pki/certs/server.crt \
    -days 0 -hours 1 \
    -sha256 \
    -extfile pki/sign-server.conf \
    -extensions v3_server

# Verify the certificate
openssl x509 -in pki/certs/server.crt -text -noout

# Verify the chain
openssl verify -CAfile pki/root/root-ca.crt pki/certs/server.crt

Task 2.3: Create Client Certificate

# Generate client private key
openssl ecparam -name prime256v1 -genkey -noout -out pki/private/client.key

# Create client CSR configuration
cat > pki/client.conf << 'EOF'
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = payment-worker

[v3_req]
subjectAltName = URI:spiffe://mesh.local/service/payment-worker
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
basicConstraints = CA:FALSE
EOF

# Generate and sign client certificate
openssl req -new -sha256 \
    -key pki/private/client.key \
    -out pki/certs/client.csr \
    -config pki/client.conf

openssl x509 -req \
    -in pki/certs/client.csr \
    -CA pki/root/root-ca.crt \
    -CAkey pki/private/root-ca.key \
    -CAcreateserial \
    -out pki/certs/client.crt \
    -days 0 -hours 1 \
    -sha256 \
    -extfile pki/client.conf \
    -extensions v3_req

Checkpoint: You have CA, server cert, and client cert. All verify against the CA.

Phase 3: Configure Go Server with RequireAndVerifyClientCert (Days 3-4)

Task 3.1: Create the mTLS Server

// server/main.go
package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {
    // Load CA certificate for client verification
    caCert, err := ioutil.ReadFile("pki/root/root-ca.crt")
    if err != nil {
        log.Fatalf("Failed to load CA cert: %v", err)
    }

    caCertPool := x509.NewCertPool()
    if !caCertPool.AppendCertsFromPEM(caCert) {
        log.Fatal("Failed to parse CA cert")
    }

    // Load server certificate and key
    serverCert, err := tls.LoadX509KeyPair(
        "pki/certs/server.crt",
        "pki/private/server.key",
    )
    if err != nil {
        log.Fatalf("Failed to load server cert: %v", err)
    }

    // Configure TLS with mutual authentication
    tlsConfig := &tls.Config{
        Certificates: []tls.Certificate{serverCert},
        ClientCAs:    caCertPool,
        ClientAuth:   tls.RequireAndVerifyClientCert, // THE KEY SETTING!
        MinVersion:   tls.VersionTLS13,
    }

    // Create server with handler that extracts client identity
    mux := http.NewServeMux()
    mux.HandleFunc("/api/status", func(w http.ResponseWriter, r *http.Request) {
        // Extract client identity from verified certificate
        if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
            clientCert := r.TLS.PeerCertificates[0]

            // Extract SPIFFE ID from SAN URIs
            spiffeID := "unknown"
            for _, uri := range clientCert.URIs {
                if uri.Scheme == "spiffe" {
                    spiffeID = uri.String()
                    break
                }
            }

            log.Printf("Request from client: %s (CN=%s)",
                spiffeID, clientCert.Subject.CommonName)

            w.Header().Set("Content-Type", "application/json")
            fmt.Fprintf(w, `{"status":"healthy","caller":"%s"}`, spiffeID)
        }
    })

    server := &http.Server{
        Addr:      ":9443",
        Handler:   mux,
        TLSConfig: tlsConfig,
    }

    log.Println("Starting mTLS server on :9443")
    log.Println("Client certificate: REQUIRED")

    // ListenAndServeTLS with empty strings because certs are in TLSConfig
    log.Fatal(server.ListenAndServeTLS("", ""))
}

Task 3.2: Create the mTLS Client

// client/main.go
package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {
    // Load CA certificate for server verification
    caCert, err := ioutil.ReadFile("pki/root/root-ca.crt")
    if err != nil {
        log.Fatalf("Failed to load CA cert: %v", err)
    }

    caCertPool := x509.NewCertPool()
    if !caCertPool.AppendCertsFromPEM(caCert) {
        log.Fatal("Failed to parse CA cert")
    }

    // Load client certificate and key
    clientCert, err := tls.LoadX509KeyPair(
        "pki/certs/client.crt",
        "pki/private/client.key",
    )
    if err != nil {
        log.Fatalf("Failed to load client cert: %v", err)
    }

    // Configure TLS with client certificate
    tlsConfig := &tls.Config{
        Certificates: []tls.Certificate{clientCert},
        RootCAs:      caCertPool,
        MinVersion:   tls.VersionTLS13,
    }

    // Create HTTP client with mTLS
    client := &http.Client{
        Transport: &http.Transport{
            TLSClientConfig: tlsConfig,
        },
    }

    // Make request
    resp, err := client.Get("https://localhost:9443/api/status")
    if err != nil {
        log.Fatalf("Request failed: %v", err)
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Printf("Response: %s\n", body)
    fmt.Printf("TLS Version: %s\n", tlsVersionName(resp.TLS.Version))
    fmt.Printf("Cipher Suite: %s\n", tls.CipherSuiteName(resp.TLS.CipherSuite))
}

func tlsVersionName(v uint16) string {
    switch v {
    case tls.VersionTLS13:
        return "TLS 1.3"
    case tls.VersionTLS12:
        return "TLS 1.2"
    default:
        return "Unknown"
    }
}

Checkpoint: Server requires client certificate. Client presents certificate. Both verify each other.

Phase 4: Programmatic Certificate Generation (Days 5-6)

Task 4.1: Build the Mini CA

// ca/ca.go
package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "math/big"
    "net/url"
    "time"
)

type CA struct {
    rootCert    *x509.Certificate
    rootKey     *ecdsa.PrivateKey
    certSerial  *big.Int
    issuedCerts map[string]*IssuedCertificate
}

func NewCA() (*CA, error) {
    // Generate CA key pair
    rootKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
    if err != nil {
        return nil, err
    }

    // Generate serial number
    serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
    if err != nil {
        return nil, err
    }

    // Create CA certificate template
    rootTemplate := &x509.Certificate{
        SerialNumber: serialNumber,
        Subject: pkix.Name{
            CommonName:   "mtls-mesh-root-ca",
            Organization: []string{"My Organization"},
        },
        NotBefore:             time.Now(),
        NotAfter:              time.Now().AddDate(10, 0, 0), // 10 years
        KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
        BasicConstraintsValid: true,
        IsCA:                  true,
        MaxPathLen:            1,
    }

    // Self-sign the CA certificate
    rootCertDER, err := x509.CreateCertificate(
        rand.Reader, rootTemplate, rootTemplate,
        &rootKey.PublicKey, rootKey,
    )
    if err != nil {
        return nil, err
    }

    rootCert, err := x509.ParseCertificate(rootCertDER)
    if err != nil {
        return nil, err
    }

    return &CA{
        rootCert:    rootCert,
        rootKey:     rootKey,
        certSerial:  big.NewInt(1),
        issuedCerts: make(map[string]*IssuedCertificate),
    }, nil
}

func (ca *CA) SignCSR(csrPEM []byte, spiffeID string, ttl time.Duration) ([]byte, error) {
    // Parse CSR
    block, _ := pem.Decode(csrPEM)
    if block == nil {
        return nil, fmt.Errorf("failed to decode CSR PEM")
    }

    csr, err := x509.ParseCertificateRequest(block.Bytes)
    if err != nil {
        return nil, fmt.Errorf("failed to parse CSR: %w", err)
    }

    // Verify CSR signature (proves key ownership)
    if err := csr.CheckSignature(); err != nil {
        return nil, fmt.Errorf("CSR signature invalid: %w", err)
    }

    // Generate serial number
    ca.certSerial = new(big.Int).Add(ca.certSerial, big.NewInt(1))

    // Parse SPIFFE ID
    spiffeURI, err := url.Parse(spiffeID)
    if err != nil {
        return nil, fmt.Errorf("invalid SPIFFE ID: %w", err)
    }

    // Create certificate template
    template := &x509.Certificate{
        SerialNumber: ca.certSerial,
        Subject:      csr.Subject,
        NotBefore:    time.Now(),
        NotAfter:     time.Now().Add(ttl),
        KeyUsage:     x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
        ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
        URIs:         []*url.URL{spiffeURI},
        DNSNames:     []string{"localhost", csr.Subject.CommonName + ".mesh.local"},
        IPAddresses:  []net.IP{net.ParseIP("127.0.0.1")},
    }

    // Sign the certificate
    certDER, err := x509.CreateCertificate(
        rand.Reader, template, ca.rootCert,
        csr.PublicKey, ca.rootKey,
    )
    if err != nil {
        return nil, fmt.Errorf("failed to sign certificate: %w", err)
    }

    // Encode to PEM
    certPEM := pem.EncodeToMemory(&pem.Block{
        Type:  "CERTIFICATE",
        Bytes: certDER,
    })

    // Track issued certificate
    ca.issuedCerts[ca.certSerial.String()] = &IssuedCertificate{
        Serial:    ca.certSerial,
        Subject:   csr.Subject.CommonName,
        SpiffeID:  spiffeID,
        IssuedAt:  time.Now(),
        ExpiresAt: time.Now().Add(ttl),
    }

    return certPEM, nil
}

func (ca *CA) RootCertPEM() []byte {
    return pem.EncodeToMemory(&pem.Block{
        Type:  "CERTIFICATE",
        Bytes: ca.rootCert.Raw,
    })
}

Checkpoint: CA can programmatically generate root cert and sign CSRs with SPIFFE IDs.

Phase 5: Automatic Certificate Rotation (Day 7)

Task 5.1: Implement Certificate Manager with Rotation

// shared/certmgr.go
package shared

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/tls"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "sync"
    "time"
)

type CertificateManager struct {
    mu          sync.RWMutex
    privateKey  *ecdsa.PrivateKey
    certificate *tls.Certificate
    caCert      *x509.Certificate
    spiffeID    string
    caURL       string
    stopCh      chan struct{}
}

func NewCertificateManager(caURL, spiffeID string, caCertPEM []byte) (*CertificateManager, error) {
    // Parse CA certificate
    block, _ := pem.Decode(caCertPEM)
    caCert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        return nil, err
    }

    cm := &CertificateManager{
        spiffeID: spiffeID,
        caURL:    caURL,
        caCert:   caCert,
        stopCh:   make(chan struct{}),
    }

    // Get initial certificate
    if err := cm.obtainCertificate(); err != nil {
        return nil, err
    }

    // Start rotation goroutine
    go cm.rotationLoop()

    return cm, nil
}

func (cm *CertificateManager) obtainCertificate() error {
    // Generate new key pair
    privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        return err
    }

    // Create CSR
    csrTemplate := &x509.CertificateRequest{
        Subject: pkix.Name{
            CommonName: extractCN(cm.spiffeID),
        },
    }

    csrDER, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, privateKey)
    if err != nil {
        return err
    }

    csrPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrDER})

    // Request certificate from CA (simplified - in reality, use HTTP client)
    certPEM, err := cm.requestCertFromCA(csrPEM)
    if err != nil {
        return err
    }

    // Parse the certificate
    block, _ := pem.Decode(certPEM)
    cert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        return err
    }

    // Create tls.Certificate
    tlsCert := &tls.Certificate{
        Certificate: [][]byte{cert.Raw, cm.caCert.Raw},
        PrivateKey:  privateKey,
        Leaf:        cert,
    }

    // Atomic swap
    cm.mu.Lock()
    cm.privateKey = privateKey
    cm.certificate = tlsCert
    cm.mu.Unlock()

    log.Printf("[CertMgr] Certificate obtained. Expires: %v", cert.NotAfter)
    return nil
}

func (cm *CertificateManager) rotationLoop() {
    for {
        cm.mu.RLock()
        cert := cm.certificate.Leaf
        cm.mu.RUnlock()

        // Calculate rotation time (50% of lifetime)
        lifetime := cert.NotAfter.Sub(cert.NotBefore)
        rotateAt := cert.NotBefore.Add(lifetime / 2)
        sleepDuration := time.Until(rotateAt)

        if sleepDuration <= 0 {
            // Already past rotation time, rotate now
            sleepDuration = time.Second
        }

        select {
        case <-time.After(sleepDuration):
            log.Println("[CertMgr] Rotation triggered")
            if err := cm.obtainCertificate(); err != nil {
                log.Printf("[CertMgr] Rotation failed: %v. Retrying in 30s", err)
                time.Sleep(30 * time.Second)
            }
        case <-cm.stopCh:
            return
        }
    }
}

// GetCertificate returns the current certificate (called by tls.Config)
func (cm *CertificateManager) GetCertificate(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
    cm.mu.RLock()
    defer cm.mu.RUnlock()
    return cm.certificate, nil
}

// GetClientCertificate returns the current certificate for client connections
func (cm *CertificateManager) GetClientCertificate(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
    cm.mu.RLock()
    defer cm.mu.RUnlock()
    return cm.certificate, nil
}

func (cm *CertificateManager) Stop() {
    close(cm.stopCh)
}

Task 5.2: Use CertificateManager in Server

// server/main.go (updated)
func main() {
    // ... load CA cert ...

    cm, err := shared.NewCertificateManager(
        "http://localhost:8443",
        "spiffe://mesh.local/service/api-server",
        caCertPEM,
    )
    if err != nil {
        log.Fatal(err)
    }
    defer cm.Stop()

    tlsConfig := &tls.Config{
        GetCertificate: cm.GetCertificate, // Dynamic certificate!
        ClientCAs:      caCertPool,
        ClientAuth:     tls.RequireAndVerifyClientCert,
        MinVersion:     tls.VersionTLS13,
    }

    // ... rest of server setup ...
}

Checkpoint: Certificates rotate automatically. New connections use fresh certs.


7. Testing Strategy

7.1 Test Categories

Category Purpose Examples
Unit Tests Test certificate parsing, SPIFFE ID validation ParseSPIFFEID correctly extracts components
Integration Tests Test full mTLS handshake Client + Server complete handshake successfully
Negative Tests Verify rejection of invalid certs Expired cert rejected, wrong CA rejected
Rotation Tests Verify seamless rotation Active connections survive rotation
Security Tests Attack simulation Man-in-the-middle fails, cert replay fails

7.2 Critical Test Cases

Test 1: Basic mTLS Handshake

# Start server and client, verify successful communication
./mtls-server &
./mtls-client
# Expected: "status: healthy, caller: spiffe://..."

Test 2: Missing Client Certificate

curl --cacert pki/root/root-ca.crt https://localhost:9443/api/status
# Expected: TLS handshake failure (bad certificate)

Test 3: Expired Certificate

# Create certificate with 1 second validity, wait, try to use
# Expected: Certificate rejected as expired

Test 4: Wrong CA

# Create separate CA, issue cert from it, try to connect
# Expected: "certificate signed by unknown authority"

Test 5: Certificate Rotation Under Load

# Start continuous request loop
while true; do ./mtls-client; sleep 0.1; done &

# Wait for rotation to occur
sleep 1800  # 30 minutes for 1-hour certs at 50% rotation

# Verify no connection failures in logs

Test 6: SAN Mismatch

# Connect to hostname not in certificate's SAN
# Expected: "x509: certificate is valid for X, not Y"

7.3 OpenSSL Verification Commands

# Verify server with mTLS
openssl s_client -connect localhost:9443 \
    -CAfile pki/root/root-ca.crt \
    -cert pki/certs/client.crt \
    -key pki/private/client.key \
    -verify_return_error

# Check certificate chain
openssl verify -CAfile pki/root/root-ca.crt pki/certs/server.crt

# View certificate details
openssl x509 -in pki/certs/server.crt -text -noout | grep -A5 "Subject Alternative Name"

# Test TLS handshake timing
time openssl s_client -connect localhost:9443 </dev/null

8. Common Pitfalls and Debugging

8.1 Certificate Chain Issues

Symptom Cause Solution
โ€œcertificate signed by unknown authorityโ€ CA not in trust store Add CA cert to RootCAs (client) or ClientCAs (server)
โ€œx509: certificate has expiredโ€ Certificate past NotAfter Issue new cert, check system clock
โ€œx509: certificate is valid for X, not Yโ€ Hostname not in SAN Add correct DNS/IP/URI to certificate
โ€œtls: bad certificateโ€ Client didnโ€™t provide cert ClientAuth must be RequireAndVerifyClientCert
โ€œx509: certificate specifies an incompatible key usageโ€ Wrong EKU Ensure serverAuth for server, clientAuth for client

8.2 Debugging TLS Handshake

// Add to tls.Config for debugging
tlsConfig := &tls.Config{
    // ... other settings ...

    // Log handshake state (Go 1.16+)
    VerifyConnection: func(cs tls.ConnectionState) error {
        log.Printf("TLS Version: %x", cs.Version)
        log.Printf("Cipher Suite: %s", tls.CipherSuiteName(cs.CipherSuite))
        log.Printf("Server Name: %s", cs.ServerName)
        for i, cert := range cs.PeerCertificates {
            log.Printf("Peer Cert %d: %s", i, cert.Subject)
            for _, uri := range cert.URIs {
                log.Printf("  SAN URI: %s", uri)
            }
        }
        return nil
    },
}

8.3 OpenSSL Debugging

# Verbose TLS handshake debug
openssl s_client -connect localhost:9443 -debug -msg

# Show all certificate extensions
openssl x509 -in cert.pem -text -noout | grep -A100 "X509v3 extensions"

# Verify certificate against CA
openssl verify -verbose -CAfile ca.crt cert.pem

# Check if cert has required EKU
openssl x509 -in cert.pem -purpose -noout

8.4 Common Go Mistakes

// WRONG: Not including intermediate certs in chain
tlsCert := tls.Certificate{
    Certificate: [][]byte{leafCert.Raw},  // Missing CA!
    PrivateKey:  key,
}

// CORRECT: Include full chain
tlsCert := tls.Certificate{
    Certificate: [][]byte{leafCert.Raw, intermediateCert.Raw},
    PrivateKey:  key,
}

// WRONG: Using RootCAs for server's client verification
tlsConfig := &tls.Config{
    RootCAs:   caCertPool,  // This is for OUTGOING connections!
    ClientAuth: tls.RequireAndVerifyClientCert,
}

// CORRECT: Use ClientCAs for verifying client certificates
tlsConfig := &tls.Config{
    ClientCAs:  caCertPool,  // This verifies INCOMING client certs
    ClientAuth: tls.RequireAndVerifyClientCert,
}

9. Extensions and Challenges

9.1 Beginner Extensions

  • Certificate Inspector CLI: Parse and display any X.509 cert with all fields
  • JSON Output: Add --format json for machine-readable CA responses
  • Certificate Logging: Log all issued certificates to a file for audit
  • Graceful Shutdown: Ensure rotation goroutine stops cleanly

9.2 Intermediate Extensions

  • Intermediate CA: Add a layer between root and end-entity certs
  • Certificate Revocation: Implement revocation API and check during handshake
  • OCSP Responder: Build a simple OCSP server for revocation checking
  • Multiple SANs: Support DNS, IP, and URI SANs from CSR
  • Key Algorithm Choice: Support RSA-2048, RSA-4096, P-256, P-384

9.3 Advanced Extensions

  • SPIRE Integration: Replace your CA with SPIRE for production-grade identity
  • Hardware Security Module (HSM): Store CA key in simulated HSM (or real PKCS#11)
  • Certificate Transparency: Log certificates to a CT log
  • mTLS Sidecar Proxy: Build an Envoy-like proxy that handles mTLS for apps
  • Workload Attestation: Implement Kubernetes or AWS attestation for bootstrapping
  • Policy Engine Integration: Only issue certs based on policy (integrate with P2)

9.4 Challenge Problems

  1. Zero-Downtime CA Rotation: How do you rotate the root CA without breaking all services?

  2. Cross-Cluster mTLS: Two SPIFFE trust domains need to communicate. Implement federation.

  3. Certificate Pinning: Implement cert pinning with graceful rotation support.

  4. Audit Trail: Every certificate action (issue, revoke, rotate) must be cryptographically logged.


10. Books That Will Help

Topic Book Chapter/Section
Zero Trust fundamentals โ€œZero Trust Networksโ€ by Gilman & Barth Chapters 4-5 (Device Trust, Application Trust)
mTLS and service mesh โ€œZero Trust Networksโ€ by Gilman & Barth Chapter 6 (Network)
TLS protocol deep dive โ€œSerious Cryptography, 2nd Edโ€ by Aumasson Chapter 13 (TLS)
X.509 and PKI โ€œSecurity in Computing, 5th Edโ€ by Pfleeger Chapter 7.2 (Public Key Infrastructure)
Applied cryptography โ€œCryptography Engineeringโ€ by Ferguson, Schneier, Kohno Chapters 16-18 (Key Management)
Go crypto/tls โ€œNetwork Programming with Goโ€ by Woodbeck Chapter 9 (TLS)
SPIFFE/SPIRE โ€œSolving the Bottom Turtleโ€ (free) spiffe.io documentation
Certificate management โ€œBulletproof SSL and TLSโ€ by Ristic Chapters on PKI deployment

11. Interview Questions

After completing this project, youโ€™ll be prepared for these questions:

Conceptual Questions

  1. โ€œExplain mutual TLS and why itโ€™s important for zero trust.โ€
    • Expected: Both parties authenticate, not just server. Eliminates network-based trust.
    • Bonus: Discuss how it prevents lateral movement after network breach.
  2. โ€œWhat happens during a TLS handshake? Walk through mTLS specifically.โ€
    • Expected: ClientHello, ServerHello, Certificates, CertificateVerify, Finished
    • Bonus: Explain CertificateRequest for client auth, key derivation
  3. โ€œHow would you implement certificate rotation without downtime?โ€
    • Expected: Use GetCertificate callback, atomic swap, overlap period
    • Bonus: Discuss connection draining, monitoring rotation success
  4. โ€œWhat is SPIFFE and how does it solve workload identity?โ€
    • Expected: SPIFFE ID format, SVIDs, platform-agnostic identity
    • Bonus: Explain SPIRE architecture, attestation plugins
  5. โ€œWhy use short-lived certificates instead of CRLs/OCSP?โ€
    • Expected: Reduces blast radius, eliminates revocation complexity
    • Bonus: Discuss 1-hour certs in Istio, SPIRE rotation

Troubleshooting Questions

  1. โ€œA client is getting โ€˜certificate signed by unknown authorityโ€™. How do you debug?โ€
    • Check if correct CA is in trust store
    • Verify certificate chain is complete
    • Check for intermediate CA issues
    • Use openssl verify to trace the problem
  2. โ€œmTLS works in staging but fails in production. What could be wrong?โ€
    • Clock skew (certificate not yet valid / expired)
    • Different CA certificates
    • Load balancer terminating TLS
    • Missing SNI in client
  3. โ€œHow would you design certificate issuance for a Kubernetes cluster?โ€
    • Use SPIRE with Kubernetes attestation
    • Service account identity maps to SPIFFE ID
    • Node agent handles attestation
    • Short-lived certs with automatic rotation

12. Self-Assessment Checklist

Before considering this project complete, verify:

Understanding

  • I can explain the difference between standard TLS and mTLS
  • I can draw the TLS handshake with client authentication
  • I understand X.509 certificate structure including SANs and EKUs
  • I can explain why short-lived certificates are preferred in Zero Trust
  • I understand the identity bootstrapping problem and solutions
  • I can explain SPIFFE IDs and their purpose

Implementation

  • My CA can generate a root certificate with correct extensions
  • My CA can sign CSRs and add SPIFFE URIs to SANs
  • My server requires and verifies client certificates
  • My client presents certificates and verifies server certificates
  • Certificate rotation works without connection interruption
  • Error handling provides useful debugging information

Security

  • Private keys never leave their generating component
  • Certificates are short-lived (1 hour default)
  • Certificate chain is complete and verifiable
  • Invalid/expired/revoked certificates are rejected
  • TLS 1.3 is enforced

Operations

  • I can debug certificate issues using OpenSSL
  • I can verify certificate chains manually
  • I understand how to monitor certificate expiry
  • I can explain how to rotate the CA key safely

13. Conclusion

This project teaches you the cryptographic foundation of Zero Trust: identity at the wire level. By building your own mTLS mesh, you understand not just how to configure TLS, but why each component exists and how they work together to eliminate network-based trust.

The skills from this project apply directly to:

  • Service mesh implementation (Istio, Linkerd, Consul Connect)
  • Cloud-native security (SPIFFE/SPIRE deployments)
  • Enterprise PKI management
  • Secure microservices architecture
  • Security engineering interviews

After completing this project, youโ€™ll never again treat TLS as a black box. Youโ€™ll understand the handshake, the certificates, the chain of trust, and how to debug when things go wrong.


This guide was expanded from ZERO_TRUST_ARCHITECTURE_DEEP_DIVE.md. For the complete learning path, see the project index.