Skip to main content

mTLS and Certificates - Quick Reference Guide

Understanding certificates and mTLS doesn't have to be complicated! 🔐 This guide explains everything with real-world analogies so you can quickly grasp how secure communication works on the internet.

What are Certificates? Think of them as digital ID cards that prove "you are who you say you are" — just like a driver's license or passport in the digital world.


🎯 Quick Comparison Table

ConceptSecurity LevelBest ForReal-World Analogy
HTTP⭐ NoneNever use in productionPostcard - anyone can read
TLS/HTTPS⭐⭐⭐⭐ HighWebsites, APIsSealed letter - only recipient can read
mTLS⭐⭐⭐⭐⭐ HighestService-to-service, bankingTwo people showing IDs to each other
Self-Signed Cert⭐⭐ LowTesting, internalHomemade ID card
CA-Signed Cert⭐⭐⭐⭐⭐ HighestPublic websitesGovernment-issued passport

📜 What are Digital Certificates?

The Analogy: Passport / Driver's License 🪪

A digital certificate is like a passport or driver's license. It contains:

  • Your name (domain name)
  • Your photo (public key)
  • Issuing authority stamp (Certificate Authority signature)
  • Expiration date
  • What you're allowed to do

Certificate Contents

┌─────────────────────────────────────────────┐
│ X.509 Certificate │
├─────────────────────────────────────────────┤
│ Subject: example.com │
│ Issuer: Let's Encrypt Authority X3 │
│ Valid From: 2026-01-01 │
│ Valid To: 2026-04-01 │
│ Public Key: [RSA 2048 bits] │
│ Signature Algorithm: SHA256-RSA │
│ Serial Number: 03:....:F4 │
│ Extensions: │
│ - Subject Alternative Names: *.example.com│
│ - Key Usage: Digital Signature, Key Enc │
└─────────────────────────────────────────────┘

Real Certificate Example (Decoded)

# View certificate from a website
openssl s_client -connect example.com:443 -showcerts

# Output shows:
Subject: CN=example.com
Issuer: CN=DigiCert TLS RSA SHA256 2020 CA1
Validity:
Not Before: Dec 1 00:00:00 2025 GMT
Not After : Dec 30 23:59:59 2026 GMT
Public Key Algorithm: rsaEncryption (2048 bit)

🔐 TLS vs mTLS - The Core Difference

Regular TLS (HTTPS) - One-Way Authentication

The Analogy: Shopping at a Store 🏪

When you walk into a store to browse:

  • ✅ The store displays its business license (server shows certificate)
  • ✅ You verify it's a legitimate business (browser verifies server certificate)
  • ❌ The store doesn't check YOUR ID just for browsing (server accepts any client)
Browser                              Website
│ │
│ 1. "Hi, I want to connect" │
│ ─────────────────────────────► │
│ │
│ 2. "Here's my certificate" │
│ ◄───────────────────────────── │
│ │
│ 3. Verify certificate is valid │
│ - Issued by trusted CA? │
│ - Not expired? │
│ - Domain matches? │
│ │
│ 4. Encrypted connection begins │
│ ◄────────────────────────────► │
│ (only browser verified server) │

Mutual TLS (mTLS) - Two-Way Authentication

The Analogy: High-Security Government Building 🏛️

Entering a classified government facility:

  • ✅ The building shows its credentials (server shows certificate)
  • ✅ You verify it's the real building (client verifies server certificate)
  • ✅ You show your security clearance ID (client shows certificate)
  • ✅ Building verifies your ID is authentic (server verifies client certificate)
Client                              Server
│ │
│ 1. "Hi, I want to connect" │
│ ─────────────────────────────► │
│ │
│ 2. "Here's my certificate" │
│ ◄───────────────────────────── │
│ │
│ 3. Verify server certificate │
│ │
│ 4. "Here's MY certificate" │
│ ─────────────────────────────► │
│ │
│ 5. Server verifies client cert │
│ │
│ 6. Encrypted connection │
│ ◄────────────────────────────► │
│ (both parties verified!) │

When to Use Each

ScenarioUse TLSUse mTLS
Public website✅ Yes❌ No
E-commerce✅ Yes❌ No
Internal APIs✅ Maybe✅ Recommended
Microservices❌ Not enough✅ Yes
Banking systems❌ Not enough✅ Yes
Healthcare data❌ Not enough✅ Yes
Mobile apps✅ Yes❌ No (cert distribution hard)

🔑 Public Key Infrastructure (PKI) Explained

The Analogy: Passport System 🌍

PKI is like the global passport system:

┌──────────────────────────────────────────────────┐
│ PKI HIERARCHY │
├──────────────────────────────────────────────────┤
│ │
│ Root CA (Root Certificate Authority) │
│ └─ Like: United Nations / Global Authority │
│ • Most trusted │
│ • Kept offline, very secure │
│ • Signs intermediate CAs │
│ │
│ Intermediate CA │
│ └─ Like: Country's Passport Office │
│ • Issues actual certificates │
│ • If compromised, only revoke this level │
│ │
│ End-Entity Certificate │
│ └─ Like: Your Passport │
│ • Used by servers/clients │
│ • Shortest validity period │
│ │
└──────────────────────────────────────────────────┘

Certificate Chain Example

Root CA: GlobalSign Root CA

├─ Intermediate CA: GlobalSign Organization Validation CA
│ │
│ ├─ End Certificate: example.com
│ └─ End Certificate: api.example.com

└─ Intermediate CA: GlobalSign Domain Validation CA

└─ End Certificate: subdomain.example.com

How Trust Works

┌──────────────────────────────────────────────────┐
│ CERTIFICATE VERIFICATION │
├──────────────────────────────────────────────────┤
│ │
│ 1. Browser receives example.com certificate │
│ │
│ 2. Check: Is it signed by a trusted CA? │
│ └─ Look in browser's trust store │
│ │
│ 3. Chain validation: │
│ example.com cert │
│ ← signed by Intermediate CA cert │
│ ← signed by Root CA cert │
│ ← Is Root CA in trust store? ✓ │
│ │
│ 4. Check: Not expired? ✓ │
│ 5. Check: Domain matches? ✓ │
│ 6. Check: Not revoked? ✓ │
│ │
│ All checks pass → Connection established 🔒 │
│ │
└──────────────────────────────────────────────────┘

📝 Certificate Lifecycle

The Analogy: Passport Lifecycle 🗓️

Just like passports, certificates have a lifecycle:

┌────────────────────────────────────────────────────┐
│ CERTIFICATE LIFECYCLE │
├────────────────────────────────────────────────────┤
│ │
│ 1. GENERATION │
│ └─ Generate private key │
│ └─ Create Certificate Signing Request (CSR) │
│ (Like passport application) │
│ │
│ 2. SIGNING │
│ └─ CA verifies identity │
│ └─ CA signs certificate │
│ (Like passport office stamping your passport)│
│ │
│ 3. DEPLOYMENT │
│ └─ Install on server │
│ └─ Configure TLS settings │
│ (Like carrying passport when traveling) │
│ │
│ 4. RENEWAL (before expiration!) │
│ └─ Generate new CSR │
│ └─ Get new certificate │
│ (Like renewing passport before it expires) │
│ │
│ 5. REVOCATION (if compromised) │
│ └─ CA adds to revocation list │
│ └─ Browsers reject immediately │
│ (Like reporting stolen passport) │
│ │
└────────────────────────────────────────────────────┘

Certificate Expiration Best Practices

Certificate TypeRecommended ValidityAuto-Renewal?
Let's Encrypt90 days✅ Yes (Certbot)
Public CA1 year (max)⚠️ Manual or automated
Internal mTLS1-6 months✅ Recommended (cert-manager)
Root CA10-20 years❌ Manual
Intermediate CA5-10 years❌ Manual

🛠️ Practical Examples

Creating a Self-Signed Certificate

# Generate private key
openssl genrsa -out server.key 2048

# Generate certificate signing request (CSR)
openssl req -new -key server.key -out server.csr \
-subj "/C=US/ST=CA/L=SF/O=MyCompany/CN=example.com"

# Self-sign the certificate (valid for 365 days)
openssl x509 -req -in server.csr -signkey server.key \
-out server.crt -days 365

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

Creating CA and Signed Certificates (For mTLS)

# Step 1: Create your own Certificate Authority (CA)
# Generate CA private key
openssl genrsa -out ca.key 4096

# Generate CA certificate
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
-subj "/CN=My Company CA"

# Step 2: Create server certificate signed by CA
# Generate server private key
openssl genrsa -out server.key 2048

# Generate server CSR
openssl req -new -key server.key -out server.csr \
-subj "/CN=api.example.com"

# Sign server certificate with CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365

# Step 3: Create client certificate (for mTLS)
# Generate client private key
openssl genrsa -out client.key 2048

# Generate client CSR
openssl req -new -key client.key -out client.csr \
-subj "/CN=client-service-1"

# Sign client certificate with CA
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out client.crt -days 365

# Now you have:
# - ca.crt (CA certificate - trust anchor)
# - server.crt + server.key (server identity)
# - client.crt + client.key (client identity)

Testing mTLS Connection

# Server side: Start server requiring client certificates
openssl s_server -accept 8443 \
-cert server.crt -key server.key \
-CAfile ca.crt -Verify 1

# Client side: Connect with client certificate
openssl s_client -connect localhost:8443 \
-cert client.crt -key client.key \
-CAfile ca.crt

# Test with curl
curl --cert client.crt --key client.key \
--cacert ca.crt https://api.example.com

🐳 mTLS in Microservices (Kubernetes Example)

The Analogy: Secure Office Building 🏢

Imagine a company headquarters where:

  • Every room (microservice) has a locked door
  • Every employee (service) has an ID badge (certificate)
  • To enter any room, both the employee and room verify each other's IDs

Using Cert-Manager in Kubernetes

# Install cert-manager (creates certificates automatically)
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager

---
# Create your own CA for internal services
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: internal-ca-issuer
spec:
ca:
secretName: internal-ca-secret

---
# Request a certificate for a service
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: payment-service-cert
namespace: production
spec:
secretName: payment-service-tls
duration: 2160h # 90 days
renewBefore: 360h # Renew 15 days before expiry
commonName: payment-service.production.svc.cluster.local
dnsNames:
- payment-service
- payment-service.production
- payment-service.production.svc
- payment-service.production.svc.cluster.local
issuerRef:
name: internal-ca-issuer
kind: ClusterIssuer

Service Configuration with mTLS

# Server deployment (requires client certificates)
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
spec:
template:
spec:
containers:
- name: payment-api
image: payment-service:1.0
env:
- name: TLS_CERT_FILE
value: /etc/certs/tls.crt
- name: TLS_KEY_FILE
value: /etc/certs/tls.key
- name: TLS_CA_FILE
value: /etc/certs/ca.crt
- name: REQUIRE_CLIENT_CERT
value: "true"
volumeMounts:
- name: tls-certs
mountPath: /etc/certs
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: payment-service-tls

🔍 Common Certificate Issues & Solutions

Issue 1: Certificate Expired ❌

Error: SSL certificate problem: certificate has expired

Real-World Analogy: Using an expired passport at airport security.

Solution:

# Check certificate expiration
openssl x509 -in cert.crt -noout -dates

# Renew certificate BEFORE expiration
# With Let's Encrypt
certbot renew

# Monitor expiration
openssl x509 -in cert.crt -noout -enddate | \
awk -F= '{print $2}' | xargs -I {} date -d {} +%s

Issue 2: Hostname Mismatch ❌

Error: SSL: certificate subject name 'example.com' does not match target host name 'www.example.com'

Real-World Analogy: Your passport says "John Smith" but ticket says "Jonathan Smith".

Solution:

# Include all possible names in certificate
# Using Subject Alternative Names (SAN)
openssl req -new -key server.key -out server.csr \
-subj "/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com,DNS:*.example.com"

Issue 3: Self-Signed Certificate Not Trusted ⚠️

Error: SSL certificate problem: self signed certificate

Real-World Analogy: Showing a homemade ID card instead of government-issued.

Solution:

# Option 1: Add to trust store (testing only!)
# Linux
sudo cp ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

# macOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca.crt

# Option 2: Tell client to trust specific CA
curl --cacert ca.crt https://example.com

# Option 3: For production, use a public CA
# Like Let's Encrypt (free!)

Issue 4: Certificate Chain Incomplete ❌

Error: SSL certificate problem: unable to get local issuer certificate

Real-World Analogy: Showing only your passport without the government stamp.

Solution:

# Server must send complete chain
# Combine certificates: [End Cert] + [Intermediate] + [Root]
cat server.crt intermediate.crt root.crt > fullchain.crt

# Verify chain
openssl verify -CAfile ca-bundle.crt server.crt

🚀 mTLS Implementation Patterns

Pattern 1: Istio Service Mesh

Automatic mTLS between all services

# Enable mTLS for all services in namespace
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT # Only mTLS allowed

---
# Traffic from external sources
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: ingress-gateway
namespace: istio-system
spec:
mtls:
mode: PERMISSIVE # Allow both mTLS and plain

Pattern 2: NGINX with mTLS

server {
listen 443 ssl;
server_name api.example.com;

# Server certificate
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;

# Enable client certificate verification
ssl_client_certificate /etc/nginx/certs/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;

# Optional: Pass client cert info to backend
location / {
proxy_pass http://backend;
proxy_set_header X-Client-Cert $ssl_client_cert;
proxy_set_header X-Client-DN $ssl_client_s_dn;
}
}

Pattern 3: Node.js mTLS Server

const https = require('https');
const fs = require('fs');

const options = {
// Server's identity
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),

// Require client certificates
ca: fs.readFileSync('ca.crt'),
requestCert: true,
rejectUnauthorized: true
};

https.createServer(options, (req, res) => {
// Access client certificate info
const clientCert = req.socket.getPeerCertificate();

if (req.client.authorized) {
console.log('Client verified:', clientCert.subject.CN);
res.writeHead(200);
res.end('Hello authenticated client!');
} else {
console.log('Client not verified');
res.writeHead(401);
res.end('Authentication required');
}
}).listen(8443);

Pattern 4: Python mTLS Client

import requests

# Make request with client certificate
response = requests.get(
'https://api.example.com',
cert=('client.crt', 'client.key'),
verify='ca.crt' # Verify server certificate
)

print(response.text)

📊 Certificate Monitoring & Management

Certificate Expiry Monitoring

# Script to check certificate expiration
#!/bin/bash

DOMAIN="example.com"
DAYS_WARN=30

# Get certificate expiration date
EXPIRY=$(echo | openssl s_client -servername $DOMAIN \
-connect $DOMAIN:443 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2)

# Convert to timestamp
EXPIRY_TS=$(date -d "$EXPIRY" +%s)
NOW_TS=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_TS - $NOW_TS) / 86400 ))

echo "Certificate expires in $DAYS_LEFT days"

if [ $DAYS_LEFT -lt $DAYS_WARN ]; then
echo "⚠️ WARNING: Certificate expires soon!"
# Send alert (email, Slack, PagerDuty, etc.)
fi

Kubernetes Certificate Monitoring

# Check certificate expiration in Kubernetes
kubectl get certificates -A

# Describe certificate details
kubectl describe certificate my-service-cert -n production

# Check cert-manager logs
kubectl logs -n cert-manager deployment/cert-manager

🎓 Quick Decision Guide

┌──────────────────────────────────────────────────────┐
│ WHICH CERTIFICATE SOLUTION? │
├──────────────────────────────────────────────────────┤
│ │
│ Public Website? │
│ └─ Use Let's Encrypt (free, automatic renewal) │
│ │
│ Internal Microservices? │
│ ├─ Use Service Mesh (Istio/Linkerd) → Auto mTLS │
│ └─ Or cert-manager in Kubernetes │
│ │
│ Testing/Development? │
│ └─ Self-signed certificates are fine │
│ │
│ High Security (Banking/Healthcare)? │
│ └─ mTLS with Hardware Security Modules (HSM) │
│ │
│ Mobile Apps? │
│ └─ Certificate Pinning + Public CA │
│ │
└──────────────────────────────────────────────────────┘

🔐 Security Best Practices

PracticeWhyHow
Use strong keysPrevent brute forceRSA 2048+ or ECDSA P-256+
Short-lived certificatesLimit damage if compromised90 days or less
Automate renewalPrevent outagesCertbot, cert-manager
Protect private keysKeys = identityFile permissions 600, HSM for production
Monitor expirationAvoid downtimeAlerting 30 days before
Use different CAsReduce single point of failureLet's Encrypt + ZeroSSL
Certificate pinningPrevent MITM attacksPin public key in mobile apps
Revocation checkingBlock compromised certsOCSP stapling
Rotate regularlyEven before expiryEvery 30-90 days
Audit certificate usageKnow what you haveCertificate inventory

📋 Quick Command Reference

Certificate Information

# View certificate from file
openssl x509 -in cert.crt -text -noout

# View certificate from server
openssl s_client -connect example.com:443 -showcerts

# Check certificate expiration
openssl x509 -in cert.crt -noout -dates

# Get certificate common name
openssl x509 -in cert.crt -noout -subject

# Get certificate SANs (Subject Alternative Names)
openssl x509 -in cert.crt -noout -ext subjectAltName

Certificate Verification

# Verify certificate chain
openssl verify -CAfile ca.crt server.crt

# Verify certificate matches private key
openssl x509 -noout -modulus -in cert.crt | openssl md5
openssl rsa -noout -modulus -in key.key | openssl md5
# MD5 hashes should match

# Test TLS connection
openssl s_client -connect example.com:443 -servername example.com

# Test mTLS connection
openssl s_client -connect example.com:443 \
-cert client.crt -key client.key -CAfile ca.crt

Certificate Conversion

# PEM to DER
openssl x509 -in cert.pem -outform der -out cert.der

# DER to PEM
openssl x509 -in cert.der -inform der -outform pem -out cert.pem

# PEM to PKCS#12 (with private key)
openssl pkcs12 -export -in cert.pem -inkey key.pem -out cert.p12

# PKCS#12 to PEM
openssl pkcs12 -in cert.p12 -out cert.pem -nodes

🛡️ Zero Trust Architecture with mTLS

The Concept: Trust Nothing, Verify Everything

Real-World Analogy: 🏛️ Government facility where even employees with badges must verify identity at every door, elevator, and floor.

Traditional Perimeter Security:
├─ Outside the castle = dangerous
├─ Inside the castle = trusted
└─ Problem: One breach = full access

Zero Trust with mTLS:
├─ No implicit trust anywhere
├─ Every service verifies every other service
├─ Every request authenticated and authorized
└─ Breach impact = minimized (only one service)

Implementation

# Policy: Only payment-service can call billing-service
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: billing-service-policy
namespace: production
spec:
selector:
matchLabels:
app: billing-service
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/payment-service"]
to:
- operation:
methods: ["POST"]
paths: ["/api/create-invoice"]

📚 Useful Tools & Resources

Tools

ToolPurposeUse Case
Let's EncryptFree CAPublic websites
cert-managerK8s certificate automationKubernetes clusters
CertbotLet's Encrypt clientServer automation
Istio/LinkerdService meshAutomatic mTLS
HashiCorp VaultSecret & cert managementEnterprise PKI
AWS ACMAWS certificate managerAWS services
CloudflareSSL/TLS proxyCDN + security

Learning Resources

ResourceLink
Let's Encrypthttps://letsencrypt.org/
cert-manager Docshttps://cert-manager.io/
SSL Labs Testhttps://www.ssllabs.com/ssltest/
TLS 1.3 RFChttps://datatracker.ietf.org/doc/html/rfc8446
NIST Guidelineshttps://csrc.nist.gov/publications/sp800-52
Istio mTLShttps://istio.io/latest/docs/concepts/security/

🎯 Real-World Use Cases

Use Case 1: E-Commerce Platform

┌──────────────────────────────────────────────┐
│ Secure Payment Processing │
├──────────────────────────────────────────────┤
│ │
│ Customer Browser (TLS) │
│ ↓ │
│ Load Balancer (TLS termination) │
│ ↓ │
│ Web Server (mTLS) ←→ Payment Service (mTLS) │
│ ↓ ↓ │
│ Database (mTLS) Bank API (mTLS) │
│ │
│ Every hop verified! │
│ │
└──────────────────────────────────────────────┘

Use Case 2: Healthcare System (HIPAA)

Hospital A (mTLS) ←→ Health Records DB (mTLS)
↕ ↕
Pharmacy (mTLS) Insurance Provider (mTLS)
↕ ↕
Lab System (mTLS) ←→ Patient Portal (TLS)

All service-to-service = mTLS
Patient-facing = TLS

Use Case 3: Banking Mobile App

Mobile App
↓ (TLS + Certificate Pinning)
API Gateway
↓ (mTLS)
Authentication Service ←→ (mTLS) ←→ Core Banking
↓ (mTLS)
Transaction Service ←→ (mTLS) ←→ Fraud Detection
↓ (mTLS)
Notification Service

⚡ Performance Considerations

TLS Overhead

AspectImpactMitigation
HandshakeInitial connection slowSession resumption, TLS 1.3
Encryption/DecryptionCPU usageHardware acceleration (AES-NI)
Certificate ValidationNetwork roundtripsOCSP stapling
mTLSAdditional handshakeKeep-alive connections

Optimization Tips

# Enable TLS session resumption (NGINX)
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

# Use modern TLS 1.3 (faster handshake)
ssl_protocols TLSv1.3;

# Enable OCSP stapling (reduce validation roundtrips)
ssl_stapling on;
ssl_stapling_verify on;

# Use efficient ciphers
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';

Certificates and mTLS are your foundation for secure communication. Implement them properly, monitor them continuously, and sleep peacefully knowing your services are authenticated! 🔐

Last updated: February 2026