TLS certificates are more than files you drop into a server. They encode identity, key usage, and trust paths that clients must validate under real-world constraints. This guide explains advanced concepts you need to run production-grade TLS with confidence. You will learn how chains are built, what fields matter, how to deploy dual RSA+ECDSA certificates, enable OCSP stapling, and harden Nginx and Kubernetes Ingress. Each section includes practical commands and configurations you can apply today.
Workflow Overview
A reliable TLS workflow avoids surprises. Use this sequence:
- Inventory: list domains, ports, and client types (browsers, APIs, IoT) and note SNI and ALPN needs.
- Key strategy: choose RSA or ECDSA (or both), key sizes, and storage (file, HSM, KMS).
- CSR and policy: set SANs, EKU, KU, and organization info. Prefer SANs over CN.
- Issuance: request certs from your CA, ensure intermediate chain is provided, and record validity periods and renewal windows.
- Install: deploy leaf cert, private key, and full chain on endpoints; enable OCSP stapling.
- Test: verify chain, hostname, EKU, OCSP, TLS versions, and ciphers.
- Observe: monitor expiry, OCSP health, and errors; rotate keys and certificates on schedule.
- Improve: adopt dual certs, enforce HSTS carefully, and tune cipher policies.
TLS Internals Deep Dive
Key X.509 fields and why they matter:
- Subject Alternative Name (SAN): the definitive place for DNS names and IPs. Modern clients validate hostnames against SAN, not CN.
- Key Usage (KU): constraints on the key. For TLS server certs, digitalSignature is required. For RSA with TLS 1.2, keyEncipherment is often used. With TLS 1.3, digitalSignature is sufficient.
- Extended Key Usage (EKU): typical server certs include serverAuth; client certs for mTLS include clientAuth.
- Basic Constraints: CA=false for leaf certs; CA=true for CAs (with pathLen if needed).
- Authority Information Access (AIA): URLs to fetch issuer certs or OCSP endpoints.
- CRL Distribution Points (CDP): where revocation lists can be fetched.
- Signature Algorithm: pairs with your key (RSA-PSS, ECDSA with P-256 or P-384). Choose modern algorithms that clients support.
- Validity: short lifetimes reduce risk and force automation, but monitor renewals.
These fields drive how clients build trust paths and decide if a certificate is acceptable for a given use.
Chain Building and Validation
Clients attempt to build a path from the leaf to a trusted root:
- Provide the full chain: leaf cert first, then intermediate(s). Do not include the root.
- Path building: clients may fetch intermediates via AIA if missing, but do not rely on it. Ship intermediates explicitly.
- Name check: hostname must match a SAN DNS entry or IP.
- EKU/KU checks: serverAuth must be present for servers; clientAuth for client certs in mTLS.
- Name constraints: some enterprise roots constrain permitted DNS name spaces; leaf SANs must fit.
- Revocation status: OCSP good, CRL not listed, or stapled proof acceptable as per client policy.
- Time validity: certificate not before/after windows must include current time.
- Algorithm agility: ensure the chain uses algorithms supported by your client base.
Keys, Algorithms, Dual Stack
Algorithm choices:
- RSA: widely compatible. Use 2048 or 3072 bits. Works with RSA-PSS signatures in modern stacks.
- ECDSA: faster handshakes and smaller certs. Use P-256 (prime256v1) or P-384.
Dual-cert deployment:
- Serve an ECDSA and an RSA certificate for the same names. Modern servers select ECDSA for clients that advertise support and fall back to RSA otherwise.
- Generate separate keys and CSRs. Issue two leaf certs with identical SANs.
- Configure both certs on the listener; see the Nginx example below.
OCSP, CRL, and CT
Revocation and transparency in practice:
- OCSP: online status for a cert. Enable stapling on your server to deliver the OCSP response to clients, reducing latency and avoiding soft-fail issues.
- CRL: larger lists fetched periodically. Some clients still consume CRLs; maintain reachability of CDP URLs.
- Certificate Transparency (CT): public logs of issued certs. Modern browsers require SCTs (embedded or via OCSP). Many public CAs embed SCTs by default; verify they are present.
Operational tips:
- Cache and refresh stapled OCSP responses before expiry.
- Monitor OCSP responder reachability and stapling status in production.
SNI, ALPN, TLS 1.3 Handshake
- SNI: enables multiple certs on one IP. Ensure your clients send SNI; otherwise provide a sensible default certificate.
- ALPN: application negotiation (e.g., http/1.1 or h2). Configure ALPN to match backend and client expectations.
- TLS 1.3: simpler handshake, forward secrecy by default, and different cipher naming. Prefer TLS 1.3 with a modern TLS 1.2 fallback for legacy clients.
Certificate Types and Use
- SAN certificates: list specific hostnames; preferred for clarity.
- Wildcards: *.example.com covers one level. Avoid for multi-tenant environments where key sharing is risky.
- UCC or multi-SAN: one certificate with many SANs for related services.
- mTLS: use client certificates with clientAuth EKU; the server validates client identity, often mapping to app-level authorization.
Nginx Practical Examples
Dual RSA+ECDSA, TLS 1.3, stapling, HSTS:
server {
listen 443 ssl http2;
server_name app.example.com;
# Provide both ECDSA and RSA cert chains (leaf + intermediates)
ssl_certificate /etc/nginx/certs/app-ecdsa-fullchain.pem;
ssl_certificate_key /etc/nginx/certs/app-ecdsa.key;
ssl_certificate /etc/nginx/certs/app-rsa-fullchain.pem;
ssl_certificate_key /etc/nginx/certs/app-rsa.key;
# Trust chain for OCSP stapling verification
ssl_trusted_certificate /etc/nginx/certs/issuer-chain.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off; # Let clients pick in TLS 1.3
# Reasonable TLS 1.2 ciphers (TLS 1.3 ciphers are implicit)
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384: ECDHE-ECDSA-AES128-GCM-SHA256:
ECDHE-RSA-AES256-GCM-SHA384: ECDHE-RSA-AES128-GCM-SHA256;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s; resolver_timeout 5s;
# HSTS (enable after confirming HTTPS works everywhere)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# ALPN is on by default with http2
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://backend;
}
}
mTLS on Nginx:
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/certs/api-fullchain.pem;
ssl_certificate_key /etc/nginx/certs/api.key;
ssl_trusted_certificate /etc/nginx/certs/issuer-chain.pem;
# Require client certificate
ssl_verify_client on; # or optional
ssl_client_certificate /etc/nginx/certs/clients-ca.pem; # CA that issued client certs
ssl_verify_depth 2;
location / {
# Pass client cert info to the app if needed
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_pass http://api-backend;
}
}
Kubernetes Ingress Examples
Basic TLS on Ingress-NGINX:
apiVersion: v1
kind: Secret
metadata:
name: web-tls
namespace: web
type: kubernetes.io/tls
data:
tls.crt: <base64 of fullchain.pem>
tls.key: <base64 of privkey.pem>
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ing
namespace: web
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-ciphers: |
ECDHE-ECDSA-AES256-GCM-SHA384: ECDHE-ECDSA-AES128-GCM-SHA256:
ECDHE-RSA-AES256-GCM-SHA384: ECDHE-RSA-AES128-GCM-SHA256
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: web-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 8080
mTLS with Ingress-NGINX:
apiVersion: v1
kind: Secret
metadata:
name: client-ca
namespace: web
data:
ca.crt: <base64 of clients-ca.pem>
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ing
namespace: web
annotations:
nginx.ingress.kubernetes.io/auth-tls-secret: "web/client-ca"
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
nginx.ingress.kubernetes.io/auth-tls-verify-depth: "2"
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 8080
Linux CLI Troubleshooting
Inspect a certificate file:
openssl x509 -in cert.pem -text -noout
Check a live server with SNI and OCSP status:
openssl s_client -connect app.example.com:443 -servername app.example.com -status </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
Show the full served chain:
openssl s_client -showcerts -connect app.example.com:443 -servername app.example.com </dev/null
Verify a chain against a CA bundle:
openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt fullchain.pem
Check expiry in days remaining:
END=$(openssl x509 -enddate -noout -in cert.pem | cut -d= -f2)
EXP=$(date -d "$END" +%s); NOW=$(date +%s); echo $(( (EXP-NOW)/86400 ))
Security Hardening Checklist
- Prefer TLS 1.3, keep TLS 1.2 for legacy, disable 1.0 and 1.1.
- Use ECDSA P-256 certificates with RSA fallback if your clients need it.
- Include SANs for all hostnames; do not rely on CN.
- Ensure EKU serverAuth for servers; clientAuth for mTLS clients.
- Deploy full chain (leaf + intermediates), not the root.
- Enable OCSP stapling and verify stapling health.
- Use HSTS only after confirming HTTPS is stable; consider preload later.
- Automate renewal, test 2 weeks before expiry, and monitor days remaining.
- Protect keys with least privilege and secure storage.
- For multi-tenant setups, avoid wildcards and use separate keys per tenant.
- Log TLS version, cipher, SNI, and client cert details for audits.
- Document rollback steps and keep previous certs until new ones are proven in prod.
Local Pilot Plan
Goal: deploy a single site on one Nginx host with dual RSA+ECDSA, OCSP stapling, and a measurable test plan.
Scope: one hostname, one VM or container, no load balancer changes.
Steps:
- Keys and CSRs:
openssl ecparam -name prime256v1 -genkey -noout -out app-ecdsa.key openssl req -new -key app-ecdsa.key -out app-ecdsa.csr -subj "/CN=app.example.com" -addext "subjectAltName=DNS:app.example.com"
openssl genrsa -out app-rsa.key 2048 openssl req -new -key app-rsa.key -out app-rsa.csr -subj "/CN=app.example.com" -addext "subjectAltName=DNS:app.example.com"
- Generate ECDSA key and CSR:
- Generate RSA key and CSR:
- Issue certs from your chosen CA, obtaining fullchain files and the issuer chain for stapling.
- Configure Nginx with both certs and OCSP stapling (see example above).
- Tests (measurable):
- Handshake version: confirm TLS 1.3 works, TLS 1.2 fallback present.
- Algorithm selection: verify ECDSA is served to modern clients and RSA to legacy.
- Chain correctness: openssl s_client -showcerts shows leaf and intermediates.
- OCSP stapling: -status shows a good response with a future Next Update.
- Hostname validation: SAN matches app.example.com.
- Rollout guardrails:
- Keep previous certs and configs to rollback.
- Monitor error rates and handshake failures for 24-48 hours.
- Document results and expand to additional hostnames once metrics are green.
This pilot is narrow, measurable, and easy to inspect locally before broader deployment.
Conclusion
Advanced TLS operations hinge on getting the details right: SANs and EKU for intended use, correct chain assembly, modern algorithms, stapled revocation, and hardened server configs. Start small with a focused pilot, validate with concrete commands, and then scale the pattern to more services. Revisit your cipher policy, automate renewals and monitoring, and adapt configurations to your client base. With these practices, you will ship faster, with fewer surprises, and maintain strong security posture over time.