← Tech Guides

PKI & Certificates

Trust Through Cryptographic Verification

Section 01

Quick Reference

The most-used OpenSSL commands at a glance. Copy, paste, and adapt.

Key Generation

Generate RSA Key (4096-bit)
openssl genrsa -out private.key 4096
Generate EC Key (P-256)
openssl ecparam -genkey -name prime256v1 \
  -out ec.key

CSR & Signing

Create Certificate Signing Request
openssl req -new -key private.key \
  -out request.csr
Self-Signed Certificate (1 Year)
openssl req -x509 -newkey rsa:4096 \
  -nodes -keyout key.pem \
  -out cert.pem -days 365

Certificate Inspection

View Certificate Details
openssl x509 -in cert.pem -text -noout
Check Expiration Date
openssl x509 -enddate -noout -in cert.pem
Verify Certificate Chain
openssl verify -CAfile ca.crt \
  -untrusted intermediate.crt server.crt
Test Remote Connection
openssl s_client -connect example.com:443 \
  -showcerts

Format Conversion

PEM to DER
openssl x509 -outform der \
  -in cert.pem -out cert.der
PEM to PKCS#12 (.pfx)
openssl pkcs12 -export -out cert.pfx \
  -inkey key.pem -in cert.pem
Match Private Key to Certificate
openssl x509 -noout -modulus -in cert.pem \
  | openssl md5
openssl rsa -noout -modulus -in key.pem \
  | openssl md5
Extract from PKCS#12 (.pfx)
openssl pkcs12 -in cert.pfx \
  -out cert.pem -nodes
Tip: Verify Before Deploying
Always run openssl verify on your full chain before deploying to production. A missing intermediate certificate is the most common cause of TLS errors in browsers.
Section 02

X.509 Certificate Structure

Anatomy of the digital document that binds a public key to an identity.

Certificate Fields

An X.509 certificate is an ASN.1 structure defined by RFC 5280. Every certificate contains these core fields, encoded using DER (Distinguished Encoding Rules) and typically wrapped in PEM (Base64) for transport.

Field Description Example
Version X.509 version number (v1, v2, or v3) Version: 3 (0x2)
Serial Number Unique identifier assigned by the CA; must be unique per CA 04:e3:a5:b8:...
Signature Algorithm Algorithm the CA used to sign this certificate sha256WithRSAEncryption
Issuer Distinguished Name (DN) of the CA that signed the certificate CN=Let's Encrypt Authority X3, O=Let's Encrypt
Validity Not Before and Not After timestamps (UTC) Not Before: Jan 1 00:00:00 2025 GMT
Subject Distinguished Name of the entity this certificate identifies CN=www.example.com, O=Example Inc.
Subject Public Key Info Algorithm and the actual public key bytes RSA (4096 bit) or EC (prime256v1)
Extensions v3 extension fields (see below) Basic Constraints, Key Usage, SAN, etc.

Version 1 vs. Version 3

X.509 v1 (1988)
  • Basic fields only: version, serial, issuer, validity, subject, public key
  • No extensions support
  • Cannot distinguish CA from end-entity
  • No Subject Alternative Names
  • Obsolete; do not use for new deployments
X.509 v3 (1996+)
  • All v1 fields plus arbitrary extensions
  • Critical vs. non-critical extension marking
  • Basic Constraints distinguish CA from leaf
  • SAN allows multiple DNS names and IPs
  • Industry standard; required for TLS

Critical v3 Extensions

Extensions marked as critical must be understood by the verifying application; if an extension is critical and unrecognized, the certificate must be rejected. Non-critical extensions can be safely ignored.

Extension OID Purpose Typically Critical?
Basic Constraints 2.5.29.19 Identifies whether subject is a CA; limits chain depth via pathLenConstraint Yes
Key Usage 2.5.29.15 Restricts cryptographic operations (signing, encryption, etc.) Yes
Extended Key Usage 2.5.29.37 Purpose-specific: serverAuth, clientAuth, codeSigning, etc. Varies
Subject Alternative Name 2.5.29.17 Additional identities: DNS names, IPs, email, URIs Varies
Authority Key Identifier 2.5.29.35 Identifies the CA key that signed this cert (for chain building) No
Subject Key Identifier 2.5.29.14 Hash of this cert's public key (referenced by AKI in child certs) No
CRL Distribution Points 2.5.29.31 URLs where revocation lists can be fetched No
Authority Information Access 1.3.6.1.5.5.7.1.1 OCSP responder URL and CA Issuer cert download location No

Key Usage Bits

The Key Usage extension is a 9-bit field that restricts what cryptographic operations a key may perform. Setting the wrong bits is a common source of interoperability failures.

Bit Name Used By Description
0 digitalSignature TLS servers, clients, code signing Authenticate the entity (ECDHE, DHE key exchange)
1 nonRepudiation Document signing Provide evidence that the signer cannot deny signing
2 keyEncipherment RSA key exchange in TLS Encrypt symmetric keys (RSA key transport)
3 dataEncipherment Rare; specific applications Directly encrypt user data (not key material)
4 keyAgreement DH/ECDH Key agreement protocols (Diffie-Hellman)
5 keyCertSign CA certificates only Sign other certificates (requires Basic Constraints CA:TRUE)
6 cRLSign CA certificates only Sign Certificate Revocation Lists
7 encipherOnly Combined with keyAgreement Public key for enciphering data during key agreement only
8 decipherOnly Combined with keyAgreement Public key for deciphering data during key agreement only
Common Key Usage Profiles
TLS Server (RSA): digitalSignature, keyEncipherment
TLS Server (ECDSA): digitalSignature
CA Certificate: keyCertSign, cRLSign
Code Signing: digitalSignature

Extended Key Usage OIDs

While Key Usage restricts cryptographic operations, Extended Key Usage (EKU) restricts the application-level purpose. OIDs are dot-delimited numbers from the ITU/ISO object identifier tree.

Purpose OID Short Name Use Case
TLS Server Authentication 1.3.6.1.5.5.7.3.1 serverAuth HTTPS servers, any TLS server endpoint
TLS Client Authentication 1.3.6.1.5.5.7.3.2 clientAuth Mutual TLS (mTLS), VPN client certs
Code Signing 1.3.6.1.5.5.7.3.3 codeSigning Signing executables, scripts, packages
Email Protection 1.3.6.1.5.5.7.3.4 emailProtection S/MIME email signing and encryption
OCSP Signing 1.3.6.1.5.5.7.3.9 OCSPSigning Signing OCSP responses on behalf of a CA
Time Stamping 1.3.6.1.5.5.7.3.8 timeStamping Trusted timestamp authority responses
OID Format Explained
OIDs follow a hierarchical tree. For example, 2.5.29.17 (SAN) breaks down as: 2 (joint-iso-itu-t) → 5 (ds) → 29 (certificateExtension) → 17 (subjectAltName). The 1.3.6.1.5.5.7 prefix is the PKIX arc under which most internet PKI OIDs live.

Decoded Certificate Example

Output from openssl x509 -in server.crt -text -noout showing a typical TLS server certificate:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            04:e3:a5:b8:c7:2d:9f:01:aa:bb:cc:dd:ee:ff:00:11
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Let's Encrypt, CN=R3
        Validity
            Not Before: Jan 15 00:00:00 2025 GMT
            Not After : Apr 15 23:59:59 2025 GMT
        Subject: CN=www.example.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                A1:B2:C3:D4:E5:F6:01:02:03:04:05:06:07:08:09:10
            X509v3 Authority Key Identifier:
                14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D
            X509v3 Subject Alternative Name:
                DNS:www.example.com, DNS:example.com
            Authority Information Access:
                OCSP - URI:http://r3.o.lencr.org
                CA Issuers - URI:http://r3.i.lencr.org/
            X509v3 CRL Distribution Points:
                Full Name:
                    URI:http://r3.c.lencr.org/
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
Section 03

Certificate Authorities & Trust

How hierarchical trust is established, maintained, and verified across the internet.

CA Hierarchy

Public Key Infrastructure forms a tree of trust. The root CA sits at the apex, delegating signing authority through one or more intermediate CAs down to end-entity (leaf) certificates.

Root CA
Self-signed, offline, 20-30 year validity
Intermediate CA
Signed by root, 5-10 year validity
Issuing CA
Signed by intermediate, 3-5 year validity
End-Entity Certificate
Your server/client cert, 90 days – 1 year
Why Intermediate CAs Exist
Root CA private keys are kept offline in air-gapped HSMs. If a root key were compromised, every certificate in the hierarchy would be invalid. Intermediate CAs allow day-to-day issuance while the root remains protected. If an intermediate is compromised, only that branch of the tree is revoked.

CA Roles Explained

Role Key Storage Typical Validity Responsibilities
Root CA Offline HSM, air-gapped 20–30 years Sign intermediate CA certs, CRL signing, policy definition
Intermediate CA Online or semi-offline HSM 5–10 years Sign issuing CA certs, policy enforcement, scope limitation
Issuing CA Online HSM 3–5 years Sign end-entity certs, OCSP responder, CRL publication
Registration Authority No signing keys N/A Identity verification, CSR validation, policy checking

Cross-Signing

Cross-signing allows a new CA to be trusted by existing clients before its own root is widely distributed. A well-established CA signs the new CA's public key, creating an alternative trust path.

Old Root CA
Widely trusted
Cross-Sign
Signs new CA's key
New Root CA
Same key, two certs

Example: Let's Encrypt's ISRG Root X1 was cross-signed by IdenTrust's DST Root CA X3, allowing Android devices and older browsers to trust Let's Encrypt certificates before ISRG Root X1 was added to their trust stores.

Cross-Signing Is Not Cross-Certification
Cross-signing creates a second certificate for the same public key, signed by a different CA. Cross-certification (bridge CAs) is a more complex mutual trust arrangement between two independent PKI hierarchies, common in government and military networks.

Trust Store Overview

Every operating system and application maintains a curated list of trusted root CA certificates. This is the foundation of all public PKI trust decisions.

Platform Trust Store Location Management Approx. Root CAs
Windows certlm.msc / Certificate Manager Microsoft Trusted Root Program, auto-updated via Windows Update ~400
macOS / iOS Keychain Access / System Roots Apple Root Certificate Program, updated with OS releases ~175
Linux (Debian/Ubuntu) /etc/ssl/certs/ via ca-certificates Mozilla NSS trust bundle, managed by distro package ~140
Linux (RHEL/Fedora) /etc/pki/tls/certs/ Mozilla NSS via ca-certificates package ~140
Firefox Built-in NSS trust store Mozilla Root Store Policy (independent of OS) ~155
Chrome Chrome Root Store (since 2022) Google Chrome Root Program (was OS-dependent) ~140
Java / JVM $JAVA_HOME/lib/security/cacerts Oracle/OpenJDK manage; use keytool to modify ~100

Chain Building & Validation (RFC 5280)

When a client receives a server certificate, it must build a chain from the leaf up to a trusted root and validate every link. Here are the validation steps in order:

  1. Signature Verification — Verify each certificate's signature using the issuer's public key. Start from the leaf, walk up to the root.
  2. Validity Period — Check that the current time falls within notBefore and notAfter for every certificate in the chain.
  3. Revocation Status — Query OCSP responder or download CRL for each certificate. OCSP stapling lets the server provide a pre-fetched response.
  4. Name Matching — Verify the requested hostname matches either the Subject CN or a SAN entry (SAN takes precedence per RFC 6125).
  5. Basic Constraints — Ensure intermediate certs have CA:TRUE and the leaf has CA:FALSE. Respect pathLenConstraint.
  6. Key Usage — Confirm the leaf cert's Key Usage bits permit the operation (e.g., digitalSignature for ECDHE).
  7. Policy Constraints — If Certificate Policies or Policy Constraints extensions are present, verify they are satisfied.
  8. Trust Anchor — The chain must terminate at a certificate present in the local trust store. If no anchor is found, the connection is rejected.

Self-Signed vs. CA-Signed

Self-Signed Certificates
  • Issuer = Subject; signed by its own private key
  • Free and instant to generate
  • Not trusted by browsers/OS without manual import
  • No revocation infrastructure
  • Appropriate for: development, internal testing, air-gapped systems
  • Trust is established out-of-band (manual distribution)
CA-Signed Certificates
  • Issuer =/= Subject; signed by a trusted CA's key
  • Free (Let's Encrypt) or paid (DigiCert, Sectigo, etc.)
  • Automatically trusted by all major browsers and OS
  • Full revocation support (OCSP, CRL)
  • Appropriate for: production, public-facing, compliance requirements
  • Trust via pre-installed root certificates in trust stores
Never Use Self-Signed in Production
Self-signed certificates in production force users to bypass security warnings, training them to ignore TLS errors. This undermines the entire purpose of PKI. Use Let's Encrypt for free, automated, CA-signed certificates even for internal services.

Adding a Custom CA to Trust Stores

For internal/enterprise CAs, you must distribute the root certificate to all client machines. Here are the commands for each platform:

Debian / Ubuntu
# Copy CA cert to the trusted directory
sudo cp corp-ca.crt \
  /usr/local/share/ca-certificates/corp-ca.crt

# Rebuild the trust store
sudo update-ca-certificates
RHEL / Fedora / CentOS
# Copy to anchors directory
sudo cp corp-ca.crt \
  /etc/pki/ca-trust/source/anchors/corp-ca.crt

# Update the consolidated bundle
sudo update-ca-trust
macOS
# Add to System keychain as trusted root
sudo security add-trusted-cert \
  -d -r trustRoot \
  -k /Library/Keychains/System.keychain \
  corp-ca.crt
Windows (PowerShell)
# Import to Trusted Root store (machine-wide)
Import-Certificate `
  -FilePath .\corp-ca.crt `
  -CertStoreLocation Cert:\LocalMachine\Root
Java (keytool)
# Import to Java trust store (cacerts)
keytool -import -trustcacerts \
  -alias corp-ca \
  -file corp-ca.crt \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit
Firefox (Manual / Policy)
# Firefox ignores OS trust stores by default.
# Option 1: Import via Preferences > Certificates
# Option 2: Deploy policies.json:
{
  "policies": {
    "Certificates": {
      "ImportEnterpriseRoots": true
    }
  }
}
Enterprise Deployment
Use configuration management (Ansible, Puppet, Chef) or Group Policy (Windows) to automate CA distribution across your fleet. Never ask users to manually install certificates.
Section 04

Certificate Lifecycle

From request to revocation: the full journey of a digital certificate.

CSR Generation Process

A Certificate Signing Request (CSR) is a signed data structure sent to a CA containing your public key, identifying information (subject DN, SANs), and a proof-of-possession signature made with the corresponding private key. The CA verifies the signature to confirm you hold the private key, then issues a certificate binding that key to your identity.

Generate CSR (Interactive)
# Generates CSR, prompts for subject fields
openssl req -new \
  -key private.key \
  -out request.csr
Generate CSR with SAN (One-Liner)
# Non-interactive with Subject Alternative Names
openssl req -new \
  -key private.key \
  -out request.csr \
  -subj "/CN=example.com/O=Example Inc./C=US" \
  -addext "subjectAltName=DNS:example.com,DNS:www.example.com,DNS:api.example.com"
Generate Key + CSR Together
# Create new key and CSR in one command
openssl req -new -newkey rsa:4096 \
  -nodes \
  -keyout private.key \
  -out request.csr \
  -subj "/CN=example.com"
Verify CSR Contents
# Inspect CSR before submitting to CA
openssl req -in request.csr \
  -text -noout -verify

# Output includes:
#   verify OK
#   Subject: CN = example.com
#   Public Key Algorithm: rsaEncryption
#   Requested Extensions:
#     X509v3 Subject Alternative Name:
#       DNS:example.com, DNS:www.example.com
CSR Best Practices
Always include SANs in your CSR. Since 2017, browsers ignore the Subject CN for hostname matching and rely exclusively on the Subject Alternative Name extension (per CA/Browser Forum Baseline Requirements). A CSR without SANs will produce a certificate that triggers browser warnings.

Certificate Issuance — Validation Levels

Certificate Authorities offer three levels of identity validation before issuing a certificate. Each level provides increasing assurance about who controls the domain and organization behind it.

Aspect DV Domain Validated OV Organization Validated EV Extended Validation
What Is Validated Domain control only (DNS or HTTP challenge) Domain control + organization identity (business registration, address) Domain control + rigorous organization vetting (legal existence, operational existence, physical address, authority of requester)
Issuance Time Minutes (automated) 1–3 business days 3–10 business days
Browser Indicator Padlock icon only Padlock icon (organization info in cert details) Padlock icon (organization info in cert details; green bar removed by most browsers since 2019)
Use Cases Blogs, personal sites, APIs, microservices, internal tools Business websites, SaaS applications, customer portals Banking, financial services, government, high-trust e-commerce
Typical Cost Free (Let's Encrypt) to $50/year $50–$200/year $150–$500+/year
Certificate Policy OID 2.23.140.1.2.1 2.23.140.1.2.2 2.23.140.1.1
DV Is Sufficient for Most Use Cases
DV certificates provide the same encryption strength as OV and EV. The difference is purely about identity assurance. With browser UI changes eliminating visible EV indicators, the practical benefit of EV has diminished. Focus your budget on automation and short-lived certificates rather than higher validation levels.

Renewal Strategies

Manual Renewal
  • Generate new CSR or reuse existing key
  • Submit to CA, complete validation again
  • Download new certificate and chain
  • Install on server, restart services
  • Verify with openssl s_client
  • Risk: human error, missed renewals, outages
Automated Renewal (ACME)
  • ACME client (certbot, acme.sh) handles everything
  • Automatic challenge completion (HTTP-01, DNS-01)
  • Certificate fetched and installed automatically
  • Post-renewal hooks reload services
  • Runs via cron or systemd timer
  • Best practice: renew at 2/3 of certificate lifetime
Certbot Auto-Renewal (Cron)
# Test renewal without making changes
certbot renew --dry-run

# Add to crontab (runs twice daily)
0 0,12 * * * certbot renew --quiet \
  --post-hook "systemctl reload nginx"
acme.sh Auto-Renewal
# Issue with auto-renewal configured
acme.sh --issue \
  -d example.com \
  -d www.example.com \
  --nginx \
  --reloadcmd "systemctl reload nginx"

# acme.sh installs its own cron job
# automatically on first use
Certificate Lifetimes Are Shrinking
The CA/Browser Forum voted in 2025 to progressively reduce maximum certificate lifetimes: 200 days by March 2026, 100 days by March 2027, and 45 days by March 2029. Automated renewal is no longer optional — it is a requirement. Organizations still using manual renewal processes must adopt ACME or a Certificate Lifecycle Management (CLM) platform now.

Revocation Methods

When a certificate's private key is compromised, the certificate must be revoked before its natural expiration. Four mechanisms exist for communicating revocation status, each with significant trade-offs.

CRL — Certificate Revocation Lists

A CRL is a signed list of revoked certificate serial numbers published by the CA at the URL specified in the certificate's CRL Distribution Points extension. Clients download the full list and cache it until the nextUpdate timestamp.

Advantages
  • Simple to implement and understand
  • Can be cached aggressively by clients
  • Works offline once downloaded
  • Delta CRLs reduce bandwidth (only changes since last full CRL)
  • No per-request load on CA infrastructure
Disadvantages
  • Full CRLs can grow very large (megabytes)
  • Revocation info is stale until next CRL is published
  • Clients that fail to fetch CRL often soft-fail (accept anyway)
  • Not real-time; typical update interval is hours to days
  • Delta CRL support is inconsistent across clients

OCSP — Online Certificate Status Protocol

OCSP (RFC 6960) provides real-time revocation checking. The client sends the certificate's serial number to the CA's OCSP responder URL and receives a signed response: good, revoked, or unknown.

Client
Sends serial number
OCSP Responder
Checks CA database
Signed Response
good / revoked / unknown
Advantages
  • Near real-time revocation status
  • Small response size (~1-2 KB vs. megabyte CRLs)
  • Per-certificate queries (no bulk download)
Disadvantages
  • Privacy concern: CA sees every site you visit
  • Adds latency to TLS connection (extra round trip)
  • Single point of failure (if responder is down, soft-fail)
  • CA must operate high-availability OCSP infrastructure

OCSP Stapling

With OCSP Stapling (RFC 6066, TLS Certificate Status Request), the server periodically fetches and caches the OCSP response, then includes ("staples") it in the TLS handshake. The client receives fresh revocation status without contacting the CA directly.

Nginx OCSP Stapling
server {
    ssl_stapling on;
    ssl_stapling_verify on;

    # CA chain for verifying OCSP response
    ssl_trusted_certificate /etc/ssl/chain.pem;

    # Resolver for OCSP responder lookup
    resolver 1.1.1.1 8.8.8.8 valid=300s;
    resolver_timeout 5s;
}
Apache OCSP Stapling
SSLUseStapling On
SSLStaplingCache \
  "shmcb:logs/ssl_stapling(32768)"
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors Off

# Optional: force staple even if
# responder is slow
SSLStaplingFakeTryLater Off
Verify OCSP Stapling
# Check if server is stapling OCSP response
openssl s_client -connect example.com:443 \
  -status -tlsextdebug 2>&1 \
  | grep "OCSP Response Status"

# Expected: OCSP Response Status: successful (0x0)
# If missing: OCSP response: no response sent

OCSP Must-Staple (Deprecated)

The OCSP Must-Staple extension (RFC 7633, OID 1.3.6.1.5.5.7.1.24) was designed to solve the soft-fail problem by requiring the server to include a valid OCSP staple. If the staple is missing, the client must hard-fail and reject the connection.

Must-Staple Is Being Abandoned
Let's Encrypt stopped supporting OCSP Must-Staple in new certificates as part of their broader OCSP deprecation. The extension created more outages than it prevented: servers with misconfigured stapling would become completely inaccessible rather than falling back gracefully. Most CAs now discourage its use.
Industry Shift: OCSP Is Fading, CRLs Are Returning
Let's Encrypt announced in November 2024 that it would end OCSP support entirely, relying instead on short-lived certificates and CRLs. Their reasoning: OCSP is a privacy leak, operationally expensive, and soft-fail behavior means it rarely prevents use of revoked certificates anyway. The industry is converging on a combination of short certificate lifetimes (reducing the revocation window) and CRLs (via CRLite and similar compressed bloom-filter approaches built into browsers). Mozilla Firefox already ships CRLite, which compresses the entire web PKI revocation dataset into ~1 MB updated every few hours.
Section 05

TLS/SSL Handshake

How client and server negotiate a secure channel, step by step.

TLS 1.2 Full Handshake

The TLS 1.2 handshake requires two round trips (2-RTT) before application data can flow. Each message serves a specific purpose in negotiating algorithms, authenticating the server, and establishing shared secrets.

1. ClientHello
Client sends: supported TLS versions, cipher suites, random bytes, SNI
2. ServerHello
Server selects: TLS version, cipher suite, sends random bytes
3. Certificate
Server sends certificate chain (leaf + intermediates)
4. ServerKeyExchange
Server's DH/ECDH parameters + signature (for PFS cipher suites)
5. CertificateRequest (Optional)
Server requests client certificate (mTLS only)
6. ServerHelloDone
Server signals end of its hello messages
7. ClientKeyExchange
Client's DH/ECDH public value or RSA-encrypted pre-master secret
8. ChangeCipherSpec + Finished
Client switches to encrypted mode, sends verification hash
9. ChangeCipherSpec + Finished
Server switches to encrypted mode, sends verification hash
Application Data
Encrypted communication begins (2-RTT total)

TLS 1.3 Simplified Handshake

TLS 1.3 (RFC 8446) dramatically simplifies the handshake to a single round trip (1-RTT) and supports zero round-trip resumption (0-RTT) for repeat connections. It removes legacy features, mandates PFS, and encrypts most handshake messages.

1. ClientHello + Key Share
Client sends: supported groups, key shares, cipher suites (all in one message)
2. ServerHello + Key Share
Server selects group, sends key share; handshake now encrypted
3. Encrypted Extensions + Certificate + Finished
Certificate, cert verify, and finished all encrypted in one flight
4. Client Finished
Client verifies server, sends its own finished message
Application Data
Encrypted communication begins (1-RTT total)
0-RTT Early Data (TLS 1.3)
Clients that have previously connected to a server can send encrypted application data in the first message using a pre-shared key (PSK) from the prior session. This is called 0-RTT or "early data." Security trade-off: 0-RTT data is not protected against replay attacks. An attacker who captures the ClientHello can resend it, potentially replaying the application request. Servers should only accept 0-RTT data for idempotent operations (e.g., GET requests) and must implement anti-replay mechanisms.
Feature TLS 1.2 TLS 1.3
Round Trips 2-RTT (full), 1-RTT (session resumption) 1-RTT (full), 0-RTT (resumption with PSK)
Cipher Suites ~37 supported suites (many weak) 5 AEAD-only suites (all strong)
Perfect Forward Secrecy Optional (depends on cipher suite) Mandatory (ECDHE/DHE only)
Key Exchange RSA, DHE, ECDHE ECDHE, DHE only (RSA key transport removed)
Handshake Encryption Certificates sent in plaintext Certificates encrypted after ServerHello
Removed Features RSA key exchange, RC4, DES, 3DES, MD5, SHA-1, compression, renegotiation
Session Resumption Session IDs, Session Tickets PSK-based (session tickets still used for PSK delivery)

Cipher Suites

A cipher suite defines the algorithms used for each phase of the TLS connection: key exchange, authentication, bulk encryption, and message integrity. Understanding the naming format helps you choose secure configurations.

TLS 1.2 Cipher Suite Format

TLS
Protocol
_
ECDHE
Key Exchange
_
RSA
Authentication
_WITH_
AES_256_GCM
Bulk Cipher
_
SHA384
MAC / PRF

TLS 1.3 Simplified Format

TLS 1.3 separates key exchange from cipher suite naming. Cipher suites only specify the AEAD algorithm and hash, because key exchange is always (EC)DHE and authentication is determined by the certificate type.

TLS
Protocol
_
AES_256_GCM
AEAD Cipher
_
SHA384
HKDF Hash

Recommended Modern Cipher Suites

Cipher Suite Protocol PFS Notes
TLS_AES_256_GCM_SHA384 TLS 1.3 Yes Strongest TLS 1.3 suite; 256-bit AES
TLS_AES_128_GCM_SHA256 TLS 1.3 Yes Default TLS 1.3 suite; excellent performance
TLS_CHACHA20_POLY1305_SHA256 TLS 1.3 Yes Preferred on devices without AES-NI hardware acceleration
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS 1.2 Yes Best TLS 1.2 suite with RSA certificates
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS 1.2 Yes Best TLS 1.2 suite with ECDSA certificates
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 TLS 1.2 Yes Excellent for mobile/ARM devices (no AES-NI needed)

Deprecated & Weak Ciphers

Cipher / Feature Status Vulnerability
TLS_RSA_WITH_AES_* (RSA key exchange) Avoid No PFS; ROBOT attack; removed in TLS 1.3
*_CBC_* (CBC mode ciphers) Avoid BEAST, Lucky13, POODLE timing attacks
RC4 Broken Statistical biases enable plaintext recovery (RFC 7465 prohibits)
3DES / DES Broken Sweet32 attack; 64-bit block size causes collisions
MD5 / SHA-1 (for signatures) Broken Collision attacks demonstrated; CAs stopped issuing SHA-1 in 2016
*_EXPORT_* (export-grade) Broken 40/56-bit keys; FREAK and Logjam attacks
*_NULL_* (null encryption) Broken No encryption at all; authentication only

Server Configuration

Nginx TLS Configuration
# Modern configuration (TLS 1.3 + strong 1.2)
ssl_protocols TLSv1.2 TLSv1.3;

ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';

# Server chooses cipher order
ssl_prefer_server_ciphers on;

# TLS 1.3 suites (configured separately)
ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
Apache TLS Configuration
# Modern configuration
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder on

SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256

# TLS 1.3 cipher suites
SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256

Perfect Forward Secrecy

Perfect Forward Secrecy (PFS) ensures that if a server's long-term private key is compromised in the future, past recorded TLS sessions cannot be decrypted. Each session generates a unique ephemeral key pair that is discarded after use.

Without PFS (RSA Key Exchange)
  • Client encrypts pre-master secret with server's RSA public key
  • Same RSA key used for every session
  • If private key is stolen, ALL past sessions can be decrypted
  • Attacker with recorded traffic + stolen key = full plaintext
  • Removed in TLS 1.3
With PFS (ECDHE / DHE)
  • Ephemeral Diffie-Hellman key pair generated per session
  • Server signs the ephemeral public key with its RSA/ECDSA cert
  • Session keys are derived from ephemeral exchange, then discarded
  • Compromised long-term key cannot decrypt past sessions
  • Mandatory in TLS 1.3
Feature ECDHE DHE
Full Name Elliptic Curve Diffie-Hellman Ephemeral Diffie-Hellman Ephemeral
Performance Fast (smaller keys, hardware acceleration) Slower (larger parameters needed for equivalent security)
Key Size for 128-bit Security 256-bit curve (P-256 / X25519) 3072-bit DH parameters
Recommended Curves X25519 (best), P-256 (NIST), P-384 ffdhe2048, ffdhe3072 (RFC 7919)
TLS 1.3 Status Primary key exchange method Supported but rarely used
Verify PFS Is Active
# Check cipher suite of a live connection
openssl s_client -connect example.com:443 \
  2>/dev/null | grep "Cipher    :"

# PFS active if result contains ECDHE or DHE:
#   Cipher: TLS_AES_256_GCM_SHA384 (TLS 1.3)
#   Cipher: ECDHE-RSA-AES256-GCM-SHA384 (TLS 1.2)

# Check server's supported key exchange
openssl s_client -connect example.com:443 \
  2>/dev/null | grep "Server Temp Key"

# Expected: Server Temp Key: X25519, 253 bits
# or:       Server Temp Key: ECDH, P-256, 256 bits
Prefer X25519 Over P-256
The X25519 curve (Curve25519) is faster, has a simpler implementation less prone to side-channel attacks, and is the default in TLS 1.3. While P-256 remains widely supported and secure, X25519 is the modern best practice for new deployments.
Section 06

Mutual TLS

Two-way certificate authentication for zero-trust architectures and service meshes.

How mTLS Works

Standard TLS authenticates only the server: the client verifies the server's certificate, but the server has no cryptographic proof of the client's identity. Mutual TLS (mTLS) adds a second direction — the server also requests and verifies a client certificate, creating two-way authentication.

Standard TLS (One-Way)
  • Client verifies server certificate
  • Server identity confirmed cryptographically
  • Client identity unknown to server
  • Client authenticated via application layer (passwords, tokens, API keys)
  • Sufficient for most public web applications
Mutual TLS (Two-Way)
  • Client verifies server certificate
  • Server verifies client certificate
  • Both parties authenticated at the transport layer
  • No passwords or tokens needed for identity
  • Required for zero-trust, service mesh, high-security APIs
1. ClientHello
Client initiates TLS handshake
2. ServerHello + Server Certificate
Server sends its cert chain (same as standard TLS)
3. CertificateRequest
Server requests client certificate (specifies acceptable CAs)
4. Client Certificate + CertificateVerify
Client sends its cert and proves possession of private key
5. Server Validates Client Cert
Chain verification, revocation check, CN/SAN extraction
6. Mutual Authentication Complete
Both sides verified; encrypted channel established

Common Use Cases

Use Case Description Example
Zero Trust Authenticate every service-to-service call regardless of network position BeyondCorp, Google's internal network
Service Mesh Sidecar proxies automatically handle mTLS between microservices Istio, Linkerd, Consul Connect
API Security Restrict API access to clients with valid certificates Banking APIs, payment gateways, partner integrations
IoT Device Auth Devices present unique certificates for identity AWS IoT Core, Azure IoT Hub
VPN / Network Access Client certificates for VPN and 802.1X network authentication OpenVPN, WireGuard (with cert wrappers), enterprise Wi-Fi

Configuration Examples

Nginx mTLS Configuration
server {
    listen 443 ssl;
    server_name api.example.com;

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

    # Client certificate verification
    ssl_client_certificate /etc/ssl/client-ca.crt;
    ssl_verify_client on;
    ssl_verify_depth  2;

    # Optional: CRL checking
    ssl_crl /etc/ssl/client-ca.crl;

    # Pass client cert info to upstream
    location / {
        proxy_set_header X-Client-DN
            $ssl_client_s_dn;
        proxy_set_header X-Client-Verify
            $ssl_client_verify;
        proxy_pass http://backend;
    }
}
Apache mTLS Configuration
<VirtualHost *:443>
    ServerName api.example.com

    # Server certificate
    SSLEngine on
    SSLCertificateFile    /etc/ssl/server.crt
    SSLCertificateKeyFile /etc/ssl/server.key

    # Client certificate verification
    SSLCACertificateFile /etc/ssl/client-ca.crt
    SSLVerifyClient require
    SSLVerifyDepth  2

    # Optional: CRL checking
    SSLCARevocationFile /etc/ssl/client-ca.crl
    SSLCARevocationCheck chain

    # Pass client cert info to app
    SSLOptions +StdEnvVars +ExportCertData
</VirtualHost>
Nginx ssl_verify_client Options
on — Require valid client cert (rejects connections without one).
optional — Request client cert but allow connections without one. Useful for endpoints that serve both authenticated and anonymous users.
optional_no_ca — Request client cert but do not verify it against the CA. Used when the application layer handles verification.

Client Certificate Generation

Generating client certificates requires a private CA (or a designated sub-CA) that the server trusts. Here is the complete workflow from CA creation to PKCS#12 bundle:

Step 1: Create Client CA
# Generate CA private key
openssl genrsa -out client-ca.key 4096

# Create self-signed CA certificate
openssl req -new -x509 \
  -key client-ca.key \
  -out client-ca.crt \
  -days 3650 \
  -subj "/CN=Client CA/O=Example Inc."
Step 2: Generate Client Key
# Generate client private key
openssl genrsa -out client.key 2048

# Or use EC key for better performance
openssl ecparam -genkey \
  -name prime256v1 \
  -out client.key
Step 3: Create Client CSR
# Generate CSR with client identity
openssl req -new \
  -key client.key \
  -out client.csr \
  -subj "/CN=service-a/O=Example Inc./OU=Engineering"
Step 4: Sign Client Certificate
# Sign with Client CA (valid 1 year)
openssl x509 -req \
  -in client.csr \
  -CA client-ca.crt \
  -CAkey client-ca.key \
  -CAcreateserial \
  -out client.crt \
  -days 365 \
  -extfile <(printf "extendedKeyUsage=clientAuth")
Step 5: Create PKCS#12 Bundle
# Bundle key + cert for browser/app import
openssl pkcs12 -export \
  -out client.p12 \
  -inkey client.key \
  -in client.crt \
  -certfile client-ca.crt \
  -name "Service A Client Cert"
Test Client Certificate
# Test mTLS connection with curl
curl --cert client.crt \
     --key client.key \
     --cacert server-ca.crt \
     https://api.example.com/health

# Or using PKCS#12 bundle
curl --cert-type P12 \
     --cert client.p12:password \
     https://api.example.com/health

Distribution Challenges

Chrome Ending Public CA clientAuth Support
Google announced that Chrome will stop trusting client certificates from public CAs for the clientAuth EKU by June 2026. Organizations relying on publicly-issued client certificates (e.g., from DigiCert or Sectigo) must migrate to private CAs for client authentication. This change does not affect server certificates or private CA-issued client certificates.
Challenge Description
Scale Issuing and tracking certificates for thousands of clients, services, or devices requires automation far beyond manual OpenSSL commands
Secure Delivery Private keys must reach the client securely; emailing PKCS#12 files is a common anti-pattern that exposes keys in transit and at rest
Lifecycle Management Tracking expiration, handling renewal, revoking compromised certs across a fleet requires centralized tooling
Key Storage Client private keys stored in filesystem are vulnerable; prefer TPM, secure enclave, or hardware tokens (PKCS#11)
Cross-Platform Support Different OS and browsers handle client cert prompts differently; user experience varies widely

Solutions

Solution How It Helps Examples
CLM Platforms Centralized issuance, renewal, revocation, and monitoring of all certificates Venafi, Keyfactor, Sectigo CLM, HashiCorp Vault PKI
Service Mesh Automatic mTLS between services via sidecar proxies; no application changes needed Istio (Citadel), Linkerd, Consul Connect
MDM (Mobile Device Management) Push client certificates to managed devices with secure key storage Jamf, Intune, Workspace ONE
SCEP / EST Protocols for automated certificate enrollment and renewal by devices SCEP (legacy, widely supported), EST (RFC 7030, modern replacement)
SPIFFE/SPIRE Workload identity framework; issues short-lived X.509 SVIDs automatically SPIRE (reference implementation), integrated into Istio, Envoy
Short-Lived Certificates Reduce Revocation Burden
When client certificates have lifetimes of hours or minutes (as with SPIFFE/SPIRE or Istio), revocation becomes less critical because compromised certificates expire before they can be meaningfully exploited. This "short-lived certificate" pattern is a cornerstone of modern zero-trust architectures.
Section 07

Certificate Formats & Encoding

From human-readable PEM to binary keystores — every format, its purpose, and how to convert between them.

Format Overview

Certificates and keys can be stored in several formats. The choice depends on the platform, the tool consuming the certificate, and whether you need to bundle the private key alongside the certificate chain.

Format Encoding Extensions Contains Platform
PEM Base64 .pem, .crt, .cer, .key Certificate, key, or chain Linux / Unix
DER Binary .der, .cer Certificate or key Windows, Java
PKCS#7 / P7B Base64 .p7b, .p7c Cert + chain (no private key) Windows, Java
PKCS#12 / PFX Binary .pfx, .p12 Cert + chain + private key Cross-platform
JKS Binary .jks Cert + key (Java keystore) Java (legacy)

PEM Format

PEM (Privacy-Enhanced Mail) is the most common certificate format on Linux/Unix systems. It is Base64-encoded DER data wrapped with plain-text header and footer lines. A single .pem file can contain multiple objects concatenated together (e.g., a certificate followed by its chain).

-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQEL
BQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5
IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYx
MDA2MTU0MzU1WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQG
... (Base64-encoded DER data) ...
-----END CERTIFICATE-----

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0Z3VS5JJcds3xfn/ygWyF8PbnGy5AHB8VBjP...
-----END RSA PRIVATE KEY-----

# Common PEM header types:
# -----BEGIN CERTIFICATE-----          (X.509 certificate)
# -----BEGIN RSA PRIVATE KEY-----      (PKCS#1 RSA key)
# -----BEGIN PRIVATE KEY-----          (PKCS#8 private key)
# -----BEGIN ENCRYPTED PRIVATE KEY---- (encrypted PKCS#8)
# -----BEGIN CERTIFICATE REQUEST-----  (CSR)
Most Common Format
PEM is the default output format for OpenSSL and the expected input for most Linux-based servers (Nginx, Apache, HAProxy). When someone says "send me the certificate," they almost always mean PEM format.

DER Format

DER (Distinguished Encoding Rules) is the binary form of a certificate. PEM is simply Base64-encoded DER with header/footer lines added. DER files contain exactly one object (certificate or key) and cannot be concatenated like PEM.

DER Characteristics
  • Raw binary ASN.1 encoding
  • Not human-readable (no BEGIN/END headers)
  • Smaller file size than PEM equivalent
  • Single object per file only
Common Usage
  • Java applications (native format for keytool)
  • Windows certificate stores
  • Embedded systems with limited storage
  • Any platform that doesn't need human inspection

PKCS#12 / PFX

PKCS#12 (also known as PFX) is a password-protected binary container that bundles a private key, its certificate, and the full certificate chain into a single file. It is the standard format for importing and exporting identity credentials across platforms.

What It Contains
  • Private key (encrypted with password)
  • End-entity certificate
  • Intermediate and root CA certificates (chain)
  • Optional friendly name / alias metadata
Common Use Cases
  • Windows IIS server certificate import
  • Importing into macOS Keychain
  • Migrating certificates between servers
  • Client certificate distribution (user + key in one file)
PFX Password Security
The PKCS#12 password protects the private key inside the container. Use a strong password and never transmit the PFX file and its password through the same channel. Legacy PFX files may use weak encryption (RC2/3DES); modern tools default to AES-256.

Java KeyStore (JKS)

JKS is a proprietary Java keystore format that stores certificates and keys, protected by a keystore password and individual key passwords. Since Java 9, PKCS#12 is the default keystore format, and JKS is considered legacy.

Deprecated Since Java 9
Oracle deprecated JKS in favor of PKCS#12 starting with Java 9 (2017). New Java applications should use -storetype PKCS12 with keytool. Existing JKS keystores can be converted using keytool -importkeystore. The JKS format is not portable outside the Java ecosystem.

Conversion Commands

A comprehensive reference for converting between certificate formats using openssl and keytool.

PEM → DER
openssl x509 -outform der \
  -in cert.pem \
  -out cert.der
DER → PEM
openssl x509 -inform der \
  -in cert.der \
  -out cert.pem
PEM → PKCS#12
openssl pkcs12 -export \
  -out cert.pfx \
  -inkey private.key \
  -in cert.pem \
  -certfile chain.pem
PKCS#12 → PEM (All-in-One)
openssl pkcs12 -in cert.pfx \
  -out all.pem \
  -nodes
PKCS#12 → Separate Key
# Extract private key only
openssl pkcs12 -in cert.pfx \
  -nocerts -nodes \
  -out private.key
PKCS#12 → Separate Cert
# Extract certificate only
openssl pkcs12 -in cert.pfx \
  -clcerts -nokeys \
  -out cert.pem
PKCS#12 → Separate Chain
# Extract CA chain only
openssl pkcs12 -in cert.pfx \
  -cacerts -nokeys \
  -out chain.pem
PEM → PKCS#7
openssl crl2pkcs7 -nocrl \
  -certfile cert.pem \
  -certfile chain.pem \
  -out cert.p7b
PKCS#7 → PEM
openssl pkcs7 \
  -print_certs \
  -in cert.p7b \
  -out cert.pem
PEM → JKS (Two-Step)
# Step 1: PEM → PKCS#12
openssl pkcs12 -export \
  -in cert.pem \
  -inkey private.key \
  -out cert.p12 \
  -name myalias

# Step 2: PKCS#12 → JKS
keytool -importkeystore \
  -srckeystore cert.p12 \
  -srcstoretype PKCS12 \
  -destkeystore keystore.jks \
  -deststoretype JKS
JKS → PKCS#12
keytool -importkeystore \
  -srckeystore keystore.jks \
  -srcstoretype JKS \
  -destkeystore keystore.p12 \
  -deststoretype PKCS12
Find Alias in Keystore
keytool -list -v \
  -keystore keystore.jks

# Show specific alias
keytool -list -v \
  -keystore keystore.jks \
  -alias myalias
Section 08

Let’s Encrypt & ACME

Free, automated, and open certificate issuance via the ACME protocol.

ACME Protocol Flow

ACME (Automatic Certificate Management Environment, RFC 8555) automates the entire certificate lifecycle. The client proves domain control to the CA, which then issues a signed certificate — all without human intervention.

1. Account Registration
Client creates an account with the ACME server (key pair + contact email)
2. Order Creation
Client submits a new order for a certificate with desired domain names
3. Authorization
Server returns challenges — one per domain — to prove control
4. Challenge Fulfillment
Client places HTTP file, DNS TXT record, or responds via TLS-ALPN
5. Certificate Issuance
CA validates challenges, signs the certificate, and returns it to the client

Challenge Types

Type Method Wildcard? Port Best For
HTTP-01 File at /.well-known/acme-challenge/ No 80 Simple web servers, most common challenge type
DNS-01 TXT record on _acme-challenge.domain Yes None Wildcards, servers without port 80, DNS API automation
TLS-ALPN-01 TLS handshake with special ALPN extension No 443 When only port 443 is available (no port 80)
Choosing a Challenge Type
Use HTTP-01 for simple setups where port 80 is open. Use DNS-01 when you need wildcard certificates or your server is behind a firewall. TLS-ALPN-01 is niche — primarily useful for reverse proxies that own port 443 but not port 80.

ACME Clients

Certbot

Obtain Certificate (Standalone)
certbot certonly --standalone \
  -d example.com \
  -d www.example.com
Obtain Certificate (Webroot)
certbot certonly --webroot \
  -w /var/www/html \
  -d example.com
Obtain Wildcard (DNS-01)
certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials \
    ~/.secrets/cloudflare.ini \
  -d '*.example.com' \
  -d example.com
Renew All Certificates
certbot renew

# Dry run (test without renewing)
certbot renew --dry-run
List Managed Certificates
certbot certificates
Revoke a Certificate
certbot revoke \
  --cert-path /etc/letsencrypt/live/example.com/cert.pem \
  --reason keycompromise
Use Staging Environment
certbot certonly --standalone \
  --staging \
  -d test.example.com

# Staging certs are not trusted
# but don't count against rate limits
Certbot with Nginx Plugin
certbot --nginx \
  -d example.com \
  -d www.example.com

# Auto-configures Nginx SSL blocks

acme.sh

Issue Certificate
acme.sh --issue \
  -d example.com \
  -w /var/www/html
Install Certificate
acme.sh --install-cert \
  -d example.com \
  --key-file /etc/ssl/key.pem \
  --fullchain-file /etc/ssl/fullchain.pem \
  --reloadcmd "systemctl reload nginx"
Renew Certificate
acme.sh --renew \
  -d example.com --force
Issue Wildcard (DNS API)
export CF_Key="your-api-key"
export CF_Email="you@example.com"

acme.sh --issue \
  --dns dns_cf \
  -d '*.example.com'

Caddy

Automatic HTTPS with Zero Config
Caddy automatically obtains and renews Let's Encrypt certificates for any domain name in its configuration. No ACME client setup, cron jobs, or renewal hooks are needed. Simply configure your domain and Caddy handles the rest:
example.com {
    reverse_proxy localhost:3000
}

Rate Limits

Let's Encrypt Rate Limits
50 certificates per registered domain per week. Renewals of existing certificates (same set of domain names) do not count toward this limit. There is also a limit of 5 failed validations per account, per hostname, per hour. Always use the staging environment for testing to avoid hitting production rate limits.

Auto-Renewal Setup

Let's Encrypt certificates are valid for 90 days. Automated renewal should run at least twice daily to ensure certificates are refreshed well before expiry (Certbot only renews within 30 days of expiration).

systemd Timer (Recommended)

certbot-renew.service
[Unit]
Description=Certbot Renewal

[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet \
  --deploy-hook "systemctl reload nginx"
certbot-renew.timer
[Unit]
Description=Run Certbot Twice Daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

[Install]
WantedBy=timers.target

Cron Job Alternative

# Run twice daily at random minutes past the hour
0 0,12 * * * root /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
Why Twice Daily?
Running renewal checks twice daily (as recommended by Let's Encrypt) ensures that even if one run fails due to a temporary network issue, the next attempt will succeed well before the certificate expires. The RandomizedDelaySec in the systemd timer spreads load across the day so all clients don't hit Let's Encrypt simultaneously.

ACME Endpoints

Environment Directory URL Purpose
Production https://acme-v02.api.letsencrypt.org/directory Trusted certificates for real deployments
Staging https://acme-staging-v02.api.letsencrypt.org/directory Testing — untrusted certs, relaxed rate limits
Section 09

Enterprise & Government PKI

On-premises certificate authorities, federal trust hierarchies, and smart card identity systems.

Microsoft AD CS

Active Directory Certificate Services (AD CS) is Microsoft's enterprise PKI platform. It integrates deeply with Active Directory for automated certificate enrollment, Group Policy distribution, and template-based issuance.

Standalone CA
  • No Active Directory integration
  • Manual certificate request and approval
  • Typically used as an offline root CA
  • Can be installed on non-domain-joined servers
  • No auto-enrollment support
Enterprise CA
  • Requires Active Directory domain membership
  • Certificate templates for standardized issuance
  • Auto-enrollment via Group Policy
  • Publishes certificates and CRLs to AD
  • Used as issuing CA in production environments

Two-Tier PKI Deployment

Offline Root CA
Standalone CA, air-gapped, signs only subordinate CA certs
Online Issuing CA
Enterprise CA, domain-joined, issues end-entity certificates
Certificate Templates
Define key usage, validity, subject format, and enrollment permissions
Auto-Enrollment (Group Policy)
Domain computers and users automatically request and receive certificates
Certificate Templates
AD CS certificate templates define the properties of issued certificates: key usage, validity period, subject name format, cryptographic provider, and which users or computers can enroll. Templates are configured in the Certificate Templates console and published to Active Directory for enterprise CAs to use.

Auto-Enrollment via Group Policy

Step Action Details
1 Configure certificate template Set permissions to allow "Autoenroll" for target users/computers
2 Publish template on Enterprise CA CA console → Certificate Templates → New → select template
3 Create or edit Group Policy Object Computer/User Configuration → Policies → Windows Settings → Security Settings → Public Key Policies
4 Enable auto-enrollment Certificate Services Client – Auto-Enrollment → Enabled; check "Renew expired" and "Update templates"
5 Apply and verify Run gpupdate /force, then verify via certlm.msc or certmgr.msc
Key Archival & Web Enrollment
Key Archival: AD CS can archive private keys for key management certificates (encryption keys), enabling recovery if a user's key is lost. This requires a Key Recovery Agent (KRA) certificate. Web Enrollment: The Certificate Enrollment Web Service (CES) and Certificate Enrollment Policy Web Service (CEP) enable certificate enrollment over HTTPS for non-domain-joined devices and cross-forest scenarios.

EJBCA & Alternatives

Solution Type License Best For
Microsoft AD CS Enterprise CA Windows Server license Windows-centric environments with Active Directory
EJBCA Enterprise CA Open source (LGPL) + Enterprise Large-scale PKI, cross-platform, regulatory compliance
step-ca Internal CA Open source (Apache 2.0) DevOps, microservices, short-lived certs, ACME for internal use
HashiCorp Vault PKI Secrets engine Open source (BSL) + Enterprise Dynamic secrets, service mesh integration, API-driven cert issuance
CFSSL CLI / API toolkit Open source (BSD) Lightweight internal CA, CI/CD pipelines, Kubernetes
Use Case Recommended Tool
Windows domain with Group Policy auto-enrollment AD CS
Multi-platform enterprise with compliance requirements (eIDAS, WebTrust) EJBCA
Internal microservices needing automated short-lived certs step-ca or Vault PKI
Kubernetes cluster certificate management CFSSL or step-ca (with cert-manager)
HashiCorp ecosystem with dynamic secrets Vault PKI

Federal PKI (FPKI)

The Federal Public Key Infrastructure (FPKI) is the U.S. government's trust framework for digital identity and certificate management. It enables secure, interoperable digital signatures and authentication across federal agencies and with external partners.

Federal Bridge CA (FBCA)

The Federal Bridge Certification Authority (FBCA) acts as a trust anchor that enables cross-certification between federal agencies, state governments, and external partners. Rather than each agency trusting every other agency's root CA, they each cross-certify with the FBCA, creating a hub-and-spoke trust model.

Federal Bridge CA (FBCA)
Hub of trust — cross-certifies with each agency CA
Agency CAs
DoD, Treasury, DHS, etc. — each with own PKI hierarchy
End-Entity Certificates
PIV cards, digital signatures, server auth, email encryption

FPKI Assurance Levels

Level Name Identity Proofing Token Use Case
1 Rudimentary Self-asserted Software Low-value transactions, public web forms
2 Basic Remote identity verification Software Internal applications, non-sensitive data access
3 Medium In-person or supervised remote Hardware or software Government email, document signing, building access
4 Medium Hardware In-person with government ID Hardware (PIV card) Logical and physical access, PIV authentication
5 High In-person with biometrics Hardware (FIPS 140-2 Level 3+) National security systems, classified networks

PIV & CAC Cards

Personal Identity Verification (PIV) and Common Access Card (CAC) are smart card-based identity credentials used across the U.S. federal government and Department of Defense.

PIV (FIPS 201-3)
  • Mandated by HSPD-12 for all federal employees and contractors
  • Smart card with PKI certificates, PIN, and biometrics
  • Used for logical access (computer login, VPN) and physical access (building entry)
  • Governed by NIST SP 800-73 (interfaces) and SP 800-76 (biometrics)
  • Contains four X.509 certificates (see table below)
CAC (Common Access Card)
  • Department of Defense (DoD) smart card identity credential
  • Functions as military ID, Geneva Convention card, and PKI token
  • Contains DoD PKI certificates for authentication and signing
  • Required for DoD network access (NIPRNet, SIPRNet portals)
  • Transitioning toward PIV interoperability

PIV Certificate Types

Certificate OID / Key Usage PIN Required Purpose
PIV Authentication Digital Signature, Key Agreement Yes Login to systems, VPN, web authentication (primary identity cert)
Digital Signature Digital Signature, Non-Repudiation Yes (always) Signing emails and documents (legally binding)
Key Management Key Encipherment, Key Agreement Yes Encrypting email (S/MIME) and files; key is archivable for recovery
Card Authentication Digital Signature No Contactless physical access (door readers); no PIN needed for speed

NIST SP 800-57: Key Management

NIST Special Publication 800-57 provides comprehensive recommendations for cryptographic key management, including key lifecycle, algorithm selection, and cryptoperiods (how long a key should be used).

Key Lifecycle Stages

1. Pre-Activation
Key generated but not yet authorized for use
2. Active
Key is in normal use for cryptographic operations
3. Deactivated
Key no longer used for new operations; may still decrypt/verify existing data
4. Compromised
Key integrity or confidentiality is in doubt; revoke immediately
5. Destroyed
Key material is irreversibly erased from all storage
Cryptoperiods
A cryptoperiod is the time span during which a specific key is authorized for use. NIST SP 800-57 recommends different cryptoperiods depending on key type: 1–3 years for symmetric encryption keys, 1–3 years for private signing keys, and 1–2 years for public key transport keys. Shorter cryptoperiods limit exposure if a key is compromised.
Post-Quantum Considerations (Revision 6 Draft)
The draft Revision 6 of SP 800-57 incorporates guidance for post-quantum cryptographic algorithms (NIST PQC standards ML-KEM, ML-DSA, SLH-DSA). Organizations should begin planning hybrid deployments that pair classical algorithms with post-quantum algorithms to maintain security against future quantum computers while retaining backward compatibility.
Section 10

Key Management & HSMs

Hardware security modules, key ceremonies, and the full lifecycle of cryptographic keys.

Hardware Security Modules

A Hardware Security Module (HSM) is a dedicated, tamper-resistant physical device that generates, stores, and manages cryptographic keys. HSMs perform cryptographic operations (signing, encryption, decryption) internally so that private keys never leave the protected hardware boundary.

HSMs matter because they provide a root of trust for PKI deployments. Software-based key storage is vulnerable to memory extraction, malware, and insider threats. HSMs address these risks through tamper-resistant enclosures, dedicated cryptographic processors, and strict access controls that meet regulatory requirements such as FIPS 140, Common Criteria, and PCI DSS.

HSM Types

Type Examples Form Factor Use Case
Network-attached Thales Luna, Entrust nShield Rack-mounted appliance Enterprise, high-throughput TLS offload, CA signing
USB tokens YubiHSM 2, Luna USB USB device Offline root CA, small deployments, dev/test
Cloud HSMs AWS CloudHSM, Azure Dedicated HSM, GCP Cloud HSM Cloud service Cloud-native applications, managed infrastructure

FIPS 140 Validation Levels

Level Requirements Physical Security Common Use
Level 1 Basic requirements; at least one approved algorithm No specific physical security beyond production-grade components Software-only implementations
Level 2 Tamper-evident seals, role-based authentication Tamper-evident coatings or seals; pick-resistant locks General-purpose HSMs
Level 3 Tamper-resistant, identity-based authentication Active tamper response (zeroization); hard epoxy enclosure CA root keys, code signing (most common for CAs)
Level 4 Complete physical protection, environmental failure protection Envelope of protection; environmental failure protection (temperature, voltage) Government classified systems, high-security environments

PKCS#11 Interface

PKCS#11 (also called "Cryptoki") is the standard API for communicating with HSMs and other cryptographic tokens. Defined by RSA Laboratories (now OASIS), it provides a vendor-neutral C interface that applications use to perform key generation, signing, encryption, and other operations without direct access to key material.

Most HSM vendors ship a PKCS#11 shared library (.so on Linux, .dll on Windows) that OpenSSL, Java (via SunPKCS11 provider), and other applications load to interact with the hardware. This abstraction allows swapping HSM vendors without rewriting application code.

OpenSSL with PKCS#11 engine
# Configure OpenSSL to use HSM via PKCS#11
openssl req -new -x509 \
  -engine pkcs11 \
  -keyform engine \
  -key "pkcs11:token=MyHSM;object=root-ca-key" \
  -out root-ca.crt -days 3650 \
  -subj "/CN=Root CA"
List tokens with pkcs11-tool
# List available tokens
pkcs11-tool --module /usr/lib/libCryptoki2.so \
  --list-tokens

# List objects on a specific token
pkcs11-tool --module /usr/lib/libCryptoki2.so \
  --list-objects --login

Key Ceremony Procedures

A key ceremony is a formally documented, witnessed process for generating, activating, or revoking a critical cryptographic key—typically a root CA key. Key ceremonies enforce dual control and split knowledge to prevent any single person from accessing or misusing key material.

Dual Control
  • Two or more people must participate to complete a sensitive operation
  • No single individual can perform the action alone
  • Physical presence of multiple key custodians required
  • Enforced by HSM configuration (M-of-N quorum)
Split Knowledge
  • No single person knows the complete secret
  • Key material or activation data is divided into shares
  • Each custodian holds only their share
  • Shares are stored in sealed tamper-evident envelopes in separate safes
M-of-N Quorum
An M-of-N quorum requires that M out of N total key custodians must present their shares to activate the HSM or perform a signing operation. A common configuration is 3-of-5: five custodians each hold a share, and any three of them can authorize a key operation. This provides redundancy (two custodians can be unavailable) while maintaining security (three colluding individuals are needed to compromise the key).

Root CA Key Ceremony Steps

1. Preparation
Air-gapped room; HSM, laptop, and media verified; attendees identified and logged
2. HSM Initialization
HSM is factory-reset and initialized with a new security world / partition
3. Key Share Distribution
M-of-N custodians each receive a sealed smart card or passphrase share
4. Root Key Generation
Root CA key pair generated inside the HSM; private key never exported
5. Root Certificate Signing
Self-signed root certificate created with long validity (15–25 years)
6. Verification & Audit
Certificate hash verified; all actions logged; ceremony script signed by witnesses
7. Secure Storage
HSM powered down and stored in safe; key shares distributed to separate secure locations

Key Lifecycle

Every cryptographic key follows a lifecycle from creation to destruction. Proper lifecycle management ensures keys are used securely, rotated before compromise, and destroyed when no longer needed.

1. Generation
Key pair created using cryptographically secure random number generator (inside HSM or OS CSPRNG)
2. Distribution
Public key distributed via certificate; private key securely provisioned to authorized systems
3. Storage
Private key protected at rest: HSM, encrypted keystore, TPM, or secure enclave
4. Usage
Key performs signing, encryption, or authentication within its authorized scope
5. Rotation
New key generated and phased in before the current key's cryptoperiod expires
6. Archival
Retired key preserved in secure storage for decrypting historical data or audit purposes
7. Destruction
Key material irreversibly erased from all storage media using secure zeroization

Key Escrow vs Key Recovery

Key Escrow
  • Copy of private key stored with a trusted third party
  • Third party can decrypt data if original key holder is unavailable
  • Used for encryption keys (never for signing keys)
  • Controversial: creates a single point of compromise
  • Required in some regulatory contexts (e.g., AD CS key archival)
Key Recovery
  • Mechanism to reconstruct a key from stored recovery data
  • Often implemented with M-of-N secret sharing (Shamir's scheme)
  • No single party holds the complete key
  • Recovery requires quorum of key recovery agents
  • Preferred over escrow for security-sensitive deployments
Section 11

Advanced Topics

Code signing, S/MIME email security, certificate pinning, and Certificate Transparency.

Code Signing

Code signing uses digital signatures to verify the integrity (code has not been tampered with) and attribution (code comes from a known publisher) of software. It is a critical component of supply chain security—operating systems, app stores, and enterprise policies use code signatures to decide whether software is trusted.

Platform Signing Tools

Platform Tool Signature Format Notes
Windows signtool Authenticode (PE/COFF, MSI, MSIX) Requires code signing certificate from trusted CA; supports HSM via CSP/KSP
macOS codesign Apple Code Signing (Mach-O) Requires Apple Developer ID certificate; notarization required for distribution
Java jarsigner JAR signatures (MANIFEST.MF) Signs JAR/WAR/EAR archives; supports PKCS#11 keystores
Linux (RPM) rpmsign RPM GPG signatures Uses GPG key; verified by rpm --checksig
Linux (DEB) dpkg-sig Debian package signatures Repository-level signing via apt-secure / Release.gpg
Timestamping (RFC 3161)
A timestamp proves that code was signed before the signing certificate expired or was revoked. Without a timestamp, signatures become invalid when the certificate expires. RFC 3161 defines the Timestamp Protocol: the signing tool sends a hash to a Timestamp Authority (TSA), which returns a signed timestamp token embedded in the signature. Always use /tr (RFC 3161) rather than /t (legacy Authenticode) with signtool.
EV Code Signing
Extended Validation (EV) code signing certificates require the private key to be stored on a FIPS 140-2 Level 2+ hardware token (USB or HSM). EV-signed drivers are required for Windows kernel-mode code. EV certificates also provide immediate SmartScreen reputation on Windows, bypassing the gradual trust-building that standard certificates require.
CI/CD Integration
For automated signing pipelines, store the signing key in a cloud HSM (AWS CloudHSM, Azure Key Vault HSM, GCP Cloud HSM) and access it via PKCS#11 or the cloud provider's SDK. Never store code signing private keys as CI/CD secrets or environment variables. Use short-lived signing sessions with audit logging, and restrict signing to tagged release builds only.

S/MIME for Email

S/MIME (Secure/Multipurpose Internet Mail Extensions) uses X.509 certificates to provide digital signatures and encryption for email messages. It is the standard for enterprise email security, supported natively by Outlook, Apple Mail, and Thunderbird.

S/MIME Signing
  • Sender signs with their private key
  • Recipient verifies with sender's public key (from certificate)
  • Proves sender identity and message integrity
  • Does not hide message content
  • Certificate must have emailProtection EKU
S/MIME Encryption
  • Sender encrypts with recipient's public key
  • Recipient decrypts with their private key
  • Only the intended recipient can read the message
  • Requires sender to have recipient's certificate in advance
  • Can be combined with signing (sign-then-encrypt)

S/MIME vs PGP

Feature S/MIME PGP / GPG
Trust model Hierarchical CA (X.509 certificates) Web of Trust (decentralized)
Key exchange Certificates issued by CAs; distributed via LDAP/directory Key servers; manual key exchange
Client support Native in Outlook, Apple Mail, Thunderbird, iOS/Android Requires plugin (GPG4Win, Enigmail) or dedicated client
Enterprise fit Excellent: integrates with AD, Group Policy, MDM Limited: manual key management, no centralized policy
Complexity Moderate: requires PKI infrastructure or commercial CA High: key management burden on individual users
Certificate cost Free (Actalis) to paid (DigiCert, Sectigo) Free (self-generated keys)

Certificate Pinning

Certificate pinning restricts which certificates or public keys a client will accept for a given host, providing protection beyond standard CA-based validation. If an attacker obtains a fraudulent certificate from a compromised CA, pinning prevents it from being accepted.

HPKP Deprecated
HTTP Public Key Pinning (HPKP) was a browser-based pinning mechanism defined in RFC 7469. It was deprecated in 2018 and removed from Chrome 72 because misconfiguration could permanently brick a domain (pin the wrong key and lose access = site becomes unreachable for the pin's max-age duration). Modern pinning is done at the application level, not via HTTP headers.

Modern Pinning Approaches

Platform Mechanism Configuration
Android Network Security Configuration XML in res/xml/network_security_config.xml
iOS TrustKit / NSURLSession delegate Info.plist configuration or programmatic validation
OkHttp (Java/Kotlin) CertificatePinner Programmatic via builder API
Android network_security_config.xml
<!-- res/xml/network_security_config.xml -->
<network-security-config>
  <domain-config>
    <domain includeSubdomains="true">
      api.example.com
    </domain>
    <pin-set expiration="2025-06-01">
      <!-- Primary pin (leaf or intermediate) -->
      <pin digest="SHA-256">
        base64-encoded-sha256-of-spki
      </pin>
      <!-- Backup pin (REQUIRED) -->
      <pin digest="SHA-256">
        base64-encoded-backup-pin
      </pin>
    </pin-set>
  </domain-config>
</network-security-config>
Always Include a Backup Pin
Certificate pinning requires at least one backup pin that does not match the currently deployed certificate. If your primary certificate is compromised or expires and you have no backup pin, your application will be unable to connect. Pin to the intermediate CA's public key as a backup, or pin to a second leaf certificate stored securely offline.

Certificate Transparency (CT)

Certificate Transparency is an open framework (RFC 6962) that makes TLS certificate issuance publicly auditable. CAs must submit every certificate they issue to one or more append-only CT logs, creating a public record that domain owners and browsers can monitor for unauthorized issuance.

How CT Works

1. CA Issues Certificate
CA creates the certificate and submits it to CT logs before (pre-certificate) or after issuance
2. CT Log Returns SCT
Log appends the certificate and returns a Signed Certificate Timestamp (SCT) as proof of inclusion
3. SCT Delivered to Browser
SCT embedded in the certificate (X.509 extension), TLS handshake, or OCSP stapling response
4. Browser Verifies SCT
Browser checks SCTs are present and valid; Chrome requires SCTs from multiple independent logs
Signed Certificate Timestamps (SCTs)
An SCT is a cryptographic promise from a CT log that a certificate has been (or will be) incorporated into the log within a Maximum Merge Delay (typically 24 hours). Chrome requires certificates to include SCTs from at least two independent CT logs operated by different organizations. Certificates without valid SCTs are treated as untrusted.

Monitoring with crt.sh

crt.sh is a free CT log search engine operated by Sectigo. It aggregates data from all major CT logs and allows searching for certificates by domain name, SHA-256 fingerprint, or issuer. Use it to monitor for unauthorized certificates issued for your domains.

Search CT logs via API
# Search for all certificates issued for a domain
curl "https://crt.sh/?q=example.com&output=json" \
  | jq '.[] | {id, name_value, not_after, issuer_name}'

# Search for wildcard certificates
curl "https://crt.sh/?q=%25.example.com&output=json" \
  | jq '.[] | {id, name_value}'
Check SCTs in a live certificate
# View SCTs embedded in a certificate
openssl s_client -connect example.com:443 \
  -servername example.com 2>/dev/null \
  | openssl x509 -noout -text \
  | grep -A 5 "CT Precertificate SCTs"
Section 12

Troubleshooting

Common certificate errors, their causes, and how to fix them.

"certificate verify failed"

Error: SSL: CERTIFICATE_VERIFY_FAILED
Cause: The server's certificate is not trusted by the client. This can mean the CA is not in the trust store, the certificate has expired, or the chain is incomplete (missing intermediates).

Debug:
Diagnose with openssl s_client
# Connect and inspect the certificate chain
openssl s_client -connect host:443 \
  -servername host 2>&1 | head -30

# Look for:
# - "Verify return code: 0 (ok)" = chain is valid
# - "Verify return code: 20" = unable to get local issuer
# - "Verify return code: 10" = certificate has expired
Solutions
# Update system CA bundle
sudo update-ca-certificates     # Debian/Ubuntu
sudo update-ca-trust extract    # RHEL/CentOS

# Specify CA bundle explicitly
curl --cacert /path/to/ca-bundle.crt \
  https://host

# Python: set CA bundle
export REQUESTS_CA_BUNDLE=/path/to/ca.crt

"unable to get local issuer certificate"

Error: unable to get local issuer certificate (depth=0)
Cause: The server is sending its leaf certificate but not the intermediate certificate(s) needed to chain back to a trusted root. The client cannot build the full trust path.

Debug:
Inspect the chain depth
# Show all certificates the server sends
openssl s_client -connect host:443 \
  -showcerts -servername host 2>&1 \
  | grep -E "^(Certificate chain| [0-9]+ s:)"

# If depth stops at 0, intermediate is missing
# Should show: 0 (leaf) -> 1 (intermediate) -> 2 (root)
Create fullchain.pem
# Correct order: leaf first, then intermediate(s)
cat server.crt intermediate.crt > fullchain.pem

# Nginx configuration
ssl_certificate     /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/private/server.key;

# Apache configuration
SSLCertificateFile    /etc/ssl/server.crt
SSLCertificateChainFile /etc/ssl/intermediate.crt

"certificate has expired"

Error: certificate has expired (Verify return code: 10)
Cause: The server's certificate (or an intermediate in the chain) has passed its notAfter date. Alternatively, the client's system clock may be incorrect, making a valid certificate appear expired.

Debug:
Check expiration date
# Check certificate expiration from file
openssl x509 -enddate -noout -in cert.pem

# Check remote server certificate expiration
openssl s_client -connect host:443 \
  -servername host 2>/dev/null \
  | openssl x509 -noout -dates

# Check days until expiration
openssl x509 -checkend 86400 -noout -in cert.pem
# Exit 0 = valid for at least 24h; Exit 1 = expires within 24h
Solutions
# Renew with Let's Encrypt
sudo certbot renew

# Check system clock
date -u
timedatectl status

# Sync NTP if clock is wrong
sudo systemctl restart chronyd   # RHEL/CentOS
sudo systemctl restart ntp       # Debian/Ubuntu

"hostname mismatch"

Error: hostname mismatch / certificate is not valid for the requested host
Cause: The hostname you are connecting to does not appear in the certificate's Subject Alternative Name (SAN) extension. Browsers and modern TLS libraries only check the SAN field, not the Common Name (CN).

Debug:
Check SAN entries
# View SAN from a certificate file
openssl x509 -in cert.pem -noout -text \
  | grep -A 1 "Subject Alternative Name"

# View SAN from a remote server
openssl s_client -connect host:443 \
  -servername host 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName
Solution: reissue with correct SANs
# Generate CSR with multiple SANs
openssl req -new -key server.key \
  -out server.csr \
  -subj "/CN=example.com" \
  -addext "subjectAltName=\
    DNS:example.com,\
    DNS:www.example.com,\
    DNS:api.example.com"

SAN vs CN

Common Name (CN) Is Deprecated for Hostname Validation
RFC 6125 and the CA/Browser Forum Baseline Requirements specify that the Subject Alternative Name (SAN) extension is the only field used for hostname matching. The Common Name (CN) in the Subject DN is ignored by modern browsers (Chrome since 2017, Firefox since 2019). All certificates must include a SAN, even for a single hostname. Let's Encrypt and most public CAs automatically include the CN value as a SAN entry.
Check SAN and CN separately
# Show Subject (CN)
openssl x509 -in cert.pem -noout -subject

# Show SAN extension
openssl x509 -in cert.pem -noout \
  -ext subjectAltName

# Show both in one command
openssl x509 -in cert.pem -noout -text \
  | grep -E "(Subject:|DNS:)" | head -5

Clock Skew

Certificate validation depends on accurate system time. If the client's clock is significantly wrong, valid certificates may appear expired (notAfter in the past) or not yet valid (notBefore in the future). Clock skew is a common cause of certificate errors on embedded devices, VMs, and freshly provisioned servers.

NTP synchronization (Linux)
# Check current time source (chrony)
chronyc tracking
chronyc sources -v

# Force immediate sync
sudo chronyc makestep

# Restart chrony service
sudo systemctl restart chronyd

# Alternative: ntpd
ntpq -p
sudo systemctl restart ntp
NTP synchronization (Windows)
:: Check Windows time service status
w32tm /query /status

:: Force resync
w32tm /resync /force

:: Show time source
w32tm /query /source

:: Configure NTP server
w32tm /config /manualpeerlist:"pool.ntp.org" \
  /syncfromflags:manual /update

Intermediate Missing from Chain

The most common TLS misconfiguration is a server that sends only its leaf certificate without the intermediate CA certificate(s). Some clients (notably desktop browsers) work around this with AIA fetching, but most tools (curl, OpenSSL, Python, Java) will fail with "unable to get local issuer certificate."

Detect missing intermediates
# Check chain completeness
openssl s_client -connect host:443 \
  -servername host 2>&1 \
  | grep "verify error"

# Show full chain with certificate subjects
openssl s_client -connect host:443 \
  -showcerts -servername host 2>/dev/null \
  | grep "s:" | head -5

# Download missing intermediate from AIA
openssl x509 -in leaf.crt -noout -text \
  | grep "CA Issuers" \
  | sed 's/.*URI://'
Build fullchain.pem
# Order: server cert FIRST, then intermediate(s)
# Do NOT include the root CA
cat server.crt intermediate.crt > fullchain.pem

# Verify the chain
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt \
  -untrusted intermediate.crt server.crt

Web Server Configuration

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

    # fullchain.pem = server.crt + intermediate.crt
    ssl_certificate     /etc/ssl/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/server.key;
}
Apache
<VirtualHost *:443>
    ServerName example.com

    SSLCertificateFile      /etc/ssl/server.crt
    SSLCertificateKeyFile   /etc/ssl/private/server.key
    SSLCertificateChainFile /etc/ssl/intermediate.crt
</VirtualHost>

Java Truststore Issues

Java applications use a dedicated trust store (cacerts) rather than the operating system's CA bundle. This means a certificate trusted by curl or a browser may still fail in Java if the CA is not in the JVM's trust store.

PKIX path building failed
The error sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target means the server's CA certificate is not in Java's cacerts trust store. This is the most common Java TLS error and affects any Java application making HTTPS connections (Spring Boot, Maven, Gradle, Jenkins, etc.).
Locate and inspect cacerts
# Default location (Java 9+)
$JAVA_HOME/lib/security/cacerts

# Default location (Java 8 and earlier)
$JAVA_HOME/jre/lib/security/cacerts

# Default password: "changeit"

# List all trusted CAs
keytool -list -keystore \
  $JAVA_HOME/lib/security/cacerts \
  -storepass changeit | head -20

# Search for a specific CA
keytool -list -keystore \
  $JAVA_HOME/lib/security/cacerts \
  -storepass changeit \
  | grep -i "letsencrypt"
Import a CA certificate
# Import CA cert into Java trust store
keytool -import -trustcacerts \
  -alias myca \
  -file ca.crt \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit

# Verify import
keytool -list -alias myca \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit
Enable SSL debugging
# Full SSL debug output
java -Djavax.net.debug=ssl:handshake \
  -jar myapp.jar

# More targeted debugging
java -Djavax.net.debug=ssl:handshake:verbose \
  -jar myapp.jar

# Specify custom trust store at runtime
java -Djavax.net.ssl.trustStore=/path/to/custom-cacerts \
  -Djavax.net.ssl.trustStorePassword=changeit \
  -jar myapp.jar
Common Java Truststore Gotchas
Wrong JAVA_HOME: Multiple JDK/JRE installations may exist; verify which java and java -XshowSettings:property 2>&1 | grep java.home point to the same JVM whose cacerts you modified.
Permission denied: The cacerts file is owned by root; use sudo when importing certificates.
Wrong password: The default password is changeit; if it was changed, the import will fail silently or throw an exception.
JVM restart required: cacerts is loaded at JVM startup; after importing a certificate, restart the Java application.

Debugging Tools Summary

Tool Command What It Shows
openssl s_client openssl s_client -connect host:443 TLS handshake details, certificate chain, cipher negotiation, verify result
openssl verify openssl verify -CAfile ca.crt cert.crt Offline chain validation against a specified CA bundle
openssl x509 openssl x509 -in cert.pem -text Full certificate details: subject, issuer, SAN, key usage, validity dates
curl -v curl -v https://host Full TLS negotiation including protocol version, cipher, and certificate info
keytool keytool -list -keystore cacerts Java truststore contents: aliases, fingerprints, expiration dates