πŸ” DevSecOps & Infrastructure TLS 1.3 & X.509 Keystore vs Truststore Zero-Trust mTLS

TLS, Certificates & Truststores: The Production Security Guide

Every senior software engineer and DevOps architect eventually encounters the dreaded SSL: CERTIFICATE_VERIFY_FAILED or PKIX path building failed at 2 AM. This masterclass cuts through cryptographic jargon to give you a crystal-clear operational mental model: how X.509 chains work, the critical distinction between Keystores and Truststores across Java, Linux, Python, Go, and Docker, how to enforce mutual TLS (mTLS) for microservices and AI agent swarms, and battle-tested OpenSSL diagnostic recipes.

Protocol Standard
TLS 1.3
1-RTT Handshake, 0-RTT PSK & Perfect Forward Secrecy
Format Standard
X.509 v3
SAN Extensions, Intermediate & Root CAs
Service Identity
Mutual TLS
Bi-directional crypto auth for microservices & AI agents
Polyglot Scope
JVM • POSIX • OCI
Java cacerts, Linux anchors, Python certifi & Docker

01 The Core Mental Model: Keystore vs. Truststore

Architectural Foundation

The single most common source of confusion in enterprise security is mixing up Keystores and Truststores. While both often use the same cryptographic container format (like PKCS#12 or legacy JKS), their operational roles are diametrically opposed:

Identity Container πŸͺͺ

Keystore: "Who AM I?"

A Keystore stores your private credentials. It contains your private key and the corresponding public certificate (along with any intermediate certificates up to the root) that prove who you are to the outside world.

βœ“ Contains Private Keys? YES (Strictly confidential, protected by password/KMS).
• Server Usage: Loaded by web servers (Nginx, Tomcat, Envoy) to present its TLS certificate to incoming clients during the handshake.
• Client Usage: Loaded by clients during Mutual TLS (mTLS) to prove client identity to a verifying server.
• Common File Formats: keystore.p12 (PKCS#12 standard), server.key + server.crt (PEM), or keystore.jks (legacy Java).
Authorization Container πŸ›‘οΈ

Truststore: "Who Do I TRUST?"

A Truststore stores public certificates of trusted authorities. It contains the public Root Certificates and Intermediate CAs of third parties you are willing to communicate with.

βœ— Contains Private Keys? NO, NEVER. Contains only public certificates.
• Client Usage: Used by web browsers, API clients, and microservices to verify that a remote server's certificate was signed by a legitimate CA.
• Server Usage: Used by servers in mTLS to verify incoming client certificates against allowed corporate root CAs.
• Common Locations: Java cacerts, Linux /etc/ssl/certs/ca-certificates.crt, Python certifi/cacert.pem.

How Keystores and Truststores Interact in a TLS Handshake

Step 1: Identity Presentation

Server loads its Keystore (private key + public certificate chain) and sends the public certificate to the connecting Client.

Step 2: Trust Verification

Client intercepts the server's certificate and checks its Truststore. If the certificate's issuer chains to a trusted Root CA in the truststore, the connection is accepted.

Step 3: Two-Way mTLS (Optional)

In Mutual TLS, the client presents its certificate from its Keystore, and the server validates it against the server's Truststore.

02 Anatomy of an X.509 Certificate & Public Key Infrastructure (PKI)

Certificate Internals

An X.509 version 3 certificate is a standardized cryptographic binding between a subject's identity (such as a domain name or microservice ID) and a public key, signed by a trusted issuer.

Crucial X.509 Fields Explained

Subject & Subject Alternative Name (SAN):

Historically, the Common Name (CN) specified the domain. Modern TLS deprecates CN in favor of SAN. SAN allows a single certificate to secure multiple DNS names (api.aiagent.org), wildcards (*.aiagent.org), and IP addresses (10.244.0.1).

Validity Period (NotBefore & NotAfter):

Defines the exact timestamps during which the certificate is cryptographically valid. Industry browsers enforce a maximum lifespan of 398 days (moving towards 90-day standards to force automated rotation).

Basic Constraints:

Specifies whether this certificate is a Certificate Authority (CA:TRUE) or an end-entity leaf (CA:FALSE). If a leaf cert has CA:FALSE, it cannot sign other certificates.

Extended Key Usage (EKU):

Restricts what the certificate can be used for: serverAuth (TLS web server), clientAuth (mTLS client identity), or codeSigning.

The 3-Tier Hierarchical Chain of Trust

1. Root CA (Self-Signed) Validity: 15–25 Years

Kept strictly offline/air-gapped in Hardware Security Modules (HSMs). Subject and Issuer are identical. Its public key is pre-burned into operating systems (macOS, Windows, Debian, Android) and browser root stores.

↓ Signs Intermediate CA ↓
2. Intermediate CA (Issuing CA) Validity: 2–5 Years

Online CA that signs everyday server certificates (e.g. Let's Encrypt R3). Why? If an intermediate private key is compromised, the Root CA can revoke only that intermediate without breaking global trust in the Root!

↓ Signs Leaf / Server Certificate ↓
3. Leaf / End-Entity Certificate Validity: 90–398 Days

Installed on your load balancer, Nginx, or API gateway. Has CA:FALSE. The server MUST send both this leaf certificate AND the intermediate certificate during the TLS handshake.

Certificate File Formats & Encodings Cheat Sheet

Extensions & Use Cases
Format Common Extensions Encoding Type Key Contents Typical Ecosystem
PEM .pem, .crt, .cer, .key Base64 ASCII (-----BEGIN ...-----) Certs, Private Keys, or Full Chains Linux, Nginx, Apache, Python, Go
DER .der, .cer Binary (Raw ASN.1) Single Certificate or Key Windows, Java native cryptographic APIs
PKCS#12 .p12, .pfx Password-Protected Binary Bundles Cert + Private Key + CA Chain Modern Java (Java 9+ default), Azure, IIS
JKS .jks Java Proprietary Binary Keys and Certificates Legacy Java (Java 8 and earlier)

03 Modern TLS 1.3 vs. TLS 1.2: The Handshake Deep Dive

Protocol Architecture

Released in RFC 8446, TLS 1.3 eliminated two decades of legacy cryptographic vulnerabilities while cutting handshake round-trip latency in half.

Vulnerabilities Eradicated in TLS 1.3

1. Static RSA Key Exchange Banned

In TLS 1.2, servers could encrypt session keys with their static RSA private key. If an attacker recorded network traffic and stole the private key 5 years later, they could decrypt all historical traffic! TLS 1.3 mandates Ephemeral Diffie-Hellman (ECDHE), guaranteeing Perfect Forward Secrecy (PFS).

2. Vulnerable Ciphers & Hashes Purged

RC4 stream cipher, CBC-mode ciphers (vulnerable to POODLE, Lucky13 padding oracles), SHA-1, and MD5 were completely removed. Only AEAD (Authenticated Encryption with Associated Data) ciphers like AES-GCM and ChaCha20-Poly1305 remain.

3. Encrypted Certificate Transmission

In TLS 1.2, the server certificate was sent in plaintext. In TLS 1.3, the server certificate is encrypted using keys derived from the ephemeral KeyShare before being sent over the wire, protecting metadata from passive wiretapping.

The 1-RTT Handshake Sequence

1 Round Trip Time
CLIENT                                               SERVER
  |                                                     |
  |  1. ClientHello                                     |
  |     + Supported Ciphers (AES-256-GCM, etc.)         |
  |     + KeyShare (Client's Ephemeral ECDH Public Key) |
  |     + SNI: "api.aiagent.org"                        |
  | --------------------------------------------------> |
  |                                                     |
  |  2. ServerHello                                     |
  |     + Selected Cipher Suite                         |
  |     + KeyShare (Server's Ephemeral ECDH Public Key) |
  |                                                     |
  |     [--- Symmetric Handshake Keys Derived Here ---] |
  |                                                     |
  |     {EncryptedExtensions}                           |
  |     {Certificate: Fullchain.pem}                    |
  |     {CertificateVerify: Signature over transcript} |
  |     {Finished}                                      |
  | <-------------------------------------------------- |
  |                                                     |
  |  [Client verifies Certificate against Truststore]   |
  |  [Client verifies Server's transcript signature]    |
  |                                                     |
  |  3. {Finished}                                      |
  | --------------------------------------------------> |
  |                                                     |
  | <======= 4. Full Encrypted Application Data ======> |

TLS 1.3 completes in just 1 RTT before sending application payload (HTTP/2 or HTTP/3 frames). Resumed sessions can even utilize 0-RTT Early Data via Pre-Shared Keys (PSK).

04 Keystores & Truststores Across JVM, Linux, Python, Go, Node.js & Docker

Polyglot Guide

Different runtime environments locate their truststores in radically different paths. Here is how to inspect, import, and configure custom internal CAs across every major enterprise ecosystem:

1. Java / JVM (keytool & cacerts)

JVM

The JVM ships with its own default truststore at $JAVA_HOME/lib/security/cacerts (default password: changeit). To import an enterprise root CA into the JVM:

# 1. Inspect existing certificates in Java cacerts
keytool -list -v \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit | grep -i "Alias name:"

# 2. Import custom corporate Root CA into cacerts
keytool -importcert \
  -alias "corp-internal-root-ca" \
  -file internal-ca.crt \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit \
  -noprompt

# 3. Specify custom Keystore and Truststore at application startup
java -Djavax.net.ssl.trustStore=/etc/ssl/my-truststore.p12 \
     -Djavax.net.ssl.trustStorePassword=truststoreSecret \
     -Djavax.net.ssl.trustStoreType=PKCS12 \
     -Djavax.net.ssl.keyStore=/etc/ssl/my-keystore.p12 \
     -Djavax.net.ssl.keyStorePassword=keystoreSecret \
     -Djavax.net.ssl.keyStoreType=PKCS12 \
     -jar my-service.jar

2. Linux OS System Truststores

POSIX

Operating systems maintain a centralized system certificate bundle used by curl, git, and native system binaries:

# Debian / Ubuntu Systems:
# Copy certificate (.crt) into ca-certificates folder
sudo cp internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt
sudo update-ca-certificates
# Bundle is assembled at: /etc/ssl/certs/ca-certificates.crt

# RHEL / CentOS / Rocky Linux / Fedora:
# Copy certificate into the ca-trust anchors directory
sudo cp internal-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
# Bundle is assembled at: /etc/pki/tls/certs/ca-bundle.crt

# Test system trust via curl:
curl -v https://internal-service.corp.local

3. Python, Go & Node.js Trust Resolution

Runtimes

Python Trap: Python's requests and httpx rely on the certifi library, which bundles Mozilla's static list and ignores the Linux OS truststore by default!

# Python: Force requests / urllib3 / httpx to use custom CA
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

# In Python code directly:
import httpx
client = httpx.Client(verify="/path/to/internal-ca.crt")

# Node.js: Point to extra CA bundle (avoids ignoring custom CAs)
export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/internal-ca.crt

# Go: Loads system pool by default via crypto/tls
pool, _ := x509.SystemCertPool()
caCert, _ := os.ReadFile("internal-ca.crt")
pool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{RootCAs: pool}

4. Docker & Corporate Proxies (Zscaler / Netskope)

Dockerfile

Enterprise proxy inspection breaks pip install and npm install inside Docker builds. Here is the canonical multi-stage Dockerfile solution:

FROM python:3.11-slim

# 1. Copy enterprise root certificate into container
COPY ./corp-root-ca.crt /usr/local/share/ca-certificates/corp-root-ca.crt

# 2. Update Debian system truststore & configure pip / node envs
RUN apt-get update && apt-get install -y ca-certificates && \
    update-ca-certificates && \
    rm -rf /var/lib/apt/lists/*

# 3. Inform Python, cURL, and Node of the unified bundle
ENV REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
    SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
    NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt \
    PIP_CERT=/etc/ssl/certs/ca-certificates.crt

# Now pip install works behind corporate SSL-inspecting proxies!
RUN pip install --no-cache-dir fastapi uvicorn httpx

05 Mutual TLS (mTLS) for Zero-Trust AI Agents & Microservices

Zero Trust Identity

Standard TLS only authenticates the server to the client. Mutual TLS (mTLS) requires both the client and the server to cryptographically authenticate each other. In modern distributed AI systems, autonomous agents call sensitive internal tools, vector databases, and model execution nodes (vLLM, Triton). Static API tokens can be leaked or replayed; mTLS cryptographically binds identity to the transport layer.

Client: Python AI Agent Calling Internal Model Server

httpx
import httpx

# The agent loads its own identity (Keystore) AND trusted CA (Truststore)
agent_client_cert = ("certs/agent-client.crt", "certs/agent-client.key")
trusted_ca_bundle = "certs/mesh-ca-chain.crt"

# Establish zero-trust mTLS connection
with httpx.Client(cert=agent_client_cert, verify=trusted_ca_bundle) as client:
    response = client.post(
        "https://vllm-cluster.internal:8443/v1/chat/completions",
        json={
            "model": "deepseek-r1:70b",
            "messages": [{"role": "user", "content": "Execute agent task"}]
        }
    )
    print(f"Status: {response.status_code}")
    print(f"Response: {response.json()}")

If an unauthenticated agent or attacker on the internal network tries to hit the model cluster without a signed client certificate, the handshake fails immediately at layer 4.

Server: Nginx Gateway Enforcing mTLS

nginx.conf
server {
    listen 8443 ssl http2;
    server_name vllm-cluster.internal;

    # 1. Server Keystore (Server's own identity)
    ssl_certificate     /etc/ssl/server/server.crt;
    ssl_certificate_key /etc/ssl/server/server.key;

    # 2. Server Truststore for verifying incoming Clients
    ssl_client_certificate /etc/ssl/ca/agent-mesh-ca.crt;
    ssl_verify_client       on; # Rejects any client without a valid CA-signed cert!
    ssl_verify_depth        2;

    # Pass authenticated client identity to upstream application
    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header X-Client-DN $ssl_client_s_dn;
        proxy_set_header X-Client-Verify $ssl_client_verify;
    }
}

Upstream application receives X-Client-DN (e.g. CN=agent-financial-auditor) to apply fine-grained Role-Based Access Control (RBAC).

06 The OpenSSL Swiss Army Knife: Diagnostic Recipes

CLI Diagnostics

Save these exact commands. They will solve 99% of your TLS debugging tasks:

Recipe 1: Live Handshake & Full Chain Check s_client
# Connect and dump the full certificate chain served by remote host:
openssl s_client -connect api.openai.com:443 \
                 -servername api.openai.com \
                 -showcerts

# Quick test of TLS version and cipher negotiation:
openssl s_client -connect api.github.com:443 \
                 -tls1_3 \
                 < /dev/null 2>&1 | grep -E "Protocol|Cipher"
Recipe 2: Inspect Expiry, Subject & SAN x509
# Print human-readable details (Dates, Issuer, SAN):
openssl x509 -in cert.pem -text -noout

# Check exact expiration date:
openssl x509 -enddate -noout -in cert.pem

# Verify if certificate is expired right now (returns exit code 0 or 1):
openssl x509 -checkend 0 -noout -in cert.pem
Recipe 3: Does Private Key Match Certificate? Modulus Check
# Compute MD5 checksum of the public modulus for both files.
# If the outputs match, the private key belongs to the certificate!
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa  -noout -modulus -in server.key | openssl md5

# For modern ECDSA keys:
openssl x509 -in cert.pem -noout -pubkey | openssl md5
openssl ec   -in key.pem  -pubout 2>/dev/null | openssl md5
Recipe 4: Format Conversions (PEM ↔ PKCS#12) pkcs12
# Convert PEM (Cert + Key + Intermediate) to PKCS#12 bundle (.p12):
openssl pkcs12 -export \
  -out bundle.p12 \
  -inkey server.key \
  -in server.crt \
  -certfile intermediate.crt

# Extract PEM from PKCS#12 (.p12) bundle:
openssl pkcs12 -in bundle.p12 -out decrypted-bundle.pem -nodes

Generate a Local Root CA & Multi-SAN Certificate in 3 Steps

Development Script
# Step 1: Create your own local Root CA (Validity: 10 Years)
openssl req -x509 -new -nodes -newkey rsa:4096 -sha256 -days 3650 \
  -keyout local-ca.key -out local-ca.crt \
  -subj "/CN=My-Local-Development-CA/O=DevSecOps"

# Step 2: Generate Server Private Key and Certificate Signing Request (CSR)
openssl req -new -nodes -newkey rsa:2048 \
  -keyout server.key -out server.csr \
  -subj "/CN=api.local.internal"

# Step 3: Sign CSR with Local CA and inject SAN (Subject Alternative Names)
cat >> extfile.cnf <<EOF
basicConstraints = CA:FALSE
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = api.local.internal
DNS.3 = *.internal.local
IP.1 = 127.0.0.1
EOF

openssl x509 -req -in server.csr -CA local-ca.crt -CAkey local-ca.key \
  -CAcreateserial -out server.crt -days 365 -sha256 -extfile extfile.cnf

07 Troubleshooting The Top 5 Production TLS Disasters

Incident Playbook

When production alerts fire, these are the five most frequent root causes and their exact fixes:

Disaster 1: "Works in Chrome, Fails in Python / cURL / Java"

The Incomplete Chain Trap

Symptom: You visit https://api.mycompany.com in Google Chrome and see a green padlock. But when your Python backend or cURL makes a request, it crashes with unable to get local issuer certificate.

Root Cause: Web browsers support Authority Information Access (AIA) chasingβ€”they will automatically download missing intermediate certificates over HTTP or reuse cached intermediate certs from other websites. Headless clients (cURL, Python, Go, Java) do NOT do this; they strictly require the server to supply the complete certificate chain.

The Fix: In your web server configuration (Nginx, Caddy, Cloudflare), configure the full bundle (fullchain.pem containing the leaf certificate + intermediate CA certificate), NEVER just cert.pem!

Disaster 2: PKIX path building failed: SunCertPathBuilderException

JVM Truststore

Symptom: A Spring Boot or Kafka microservice crashes on startup trying to connect to internal services with validatorException: PKIX path building failed: unable to find valid certification path to requested target.

Root Cause: The target server's certificate was issued by an internal corporate CA (or self-signed CA) that does not exist inside the JVM's default $JAVA_HOME/lib/security/cacerts truststore.

The Fix: Import the internal root CA into cacerts using keytool -importcert -alias corp-ca -file ca.crt -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit, or launch the JVM with -Djavax.net.ssl.trustStore=/path/to/truststore.p12.

Disaster 3: Hostname Mismatch (x509: certificate is valid for *.corp, not v1.api.corp)

SAN & Wildcards

Symptom: Clients reject the certificate with certificate name mismatch even though the domain matches your wildcard.

Root Cause: Wildcards (RFC 6125) do not span across multiple DNS dots! A certificate with SAN *.aiagent.org is valid for api.aiagent.org, but is strictly INVALID for v1.internal.aiagent.org or aiagent.org (the root apex domain).

The Fix: Add explicit SAN entries for the apex domain and multi-level subdomains: DNS:aiagent.org, DNS:*.aiagent.org, DNS:*.internal.aiagent.org.

Disaster 4: Certificate Expired: Renewed on Disk, but Outage Continues

Process Caching

Symptom: You renewed the certificate using Certbot or cert-manager, the new files are on disk, but all external users still receive SEC_ERROR_EXPIRED_CERTIFICATE.

Root Cause: Web servers (Nginx, Envoy, Apache, Node.js) load TLS certificates into memory at daemon startup. Replacing the files on disk does not update running workers.

The Fix: Send a zero-downtime hot reload signal: nginx -s reload or configure cert-manager to trigger pod rolling reloads via stakater/Reloader whenever the backing Secret updates.

08 Automated PKI & Monitoring (ACME, cert-manager & Prometheus)

Automation Standard

Never manage production certificates manually using calendar reminders. Modern cloud engineering uses declarative automation:

Kubernetes cert-manager ClusterIssuer & Ingress

k8s YAML
# 1. ClusterIssuer for Let's Encrypt Production
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: devops@aiagent.org
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - http01:
        ingress:
          class: nginx

---
# 2. Ingress with Automatic TLS Generation & 30-Day Renewal
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: agent-api-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.aiagent.org
    secretName: api-aiagent-org-tls
  rules:
  - host: api.aiagent.org
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: agent-backend
            port:
              number: 8080

Prometheus Blackbox Expiry Alert Rule

PromQL
groups:
- name: tls_alerts
  rules:
  # Alert if certificate will expire within 14 days
  - alert: TLSCertificateExpiringSoon
    expr: (probe_ssl_earliest_cert_expiry - time()) / 86400 < 14
    for: 1h
    labels:
      severity: warning
    annotations:
      summary: "TLS certificate for {{ $labels.instance }} expiring soon"
      description: "Certificate expires in {{ $value | humanizeDuration }}. Check automated renewal!"

  # Critical alert if certificate will expire within 48 hours
  - alert: TLSCertificateExpiryCritical
    expr: (probe_ssl_earliest_cert_expiry - time()) / 86400 < 2
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "EMERGENCY: TLS certificate for {{ $labels.instance }} expires in under 48 hours"

Prometheus blackbox_exporter continuously probes the live port 443 handshake, ensuring you detect failures before your customers do.

09 Frequently Asked Questions (FAQ)

Knowledge Base
What is the difference between SSL and TLS? β†’

TLS (Transport Layer Security) is the modern, standardized successor to Netscape's legacy SSL (Secure Sockets Layer). SSL 2.0 and SSL 3.0 were cryptographically broken and officially deprecated in 2011 and 2015. Today, when people say "SSL certificate" or "SSL handshake," they almost always mean TLS 1.2 or TLS 1.3.

Why does modern TLS require Subject Alternative Name (SAN) instead of Common Name (CN)? β†’

The Common Name (CN) field in X.509 certificates was ambiguous and limited to a single string. It did not support multi-domain hosting, IP address bindings, or clear separation of wildcard boundaries. Under RFC 6125 and modern browser rules (Chrome, Firefox, Safari), the CN is completely ignored during hostname verification if SAN extensions are present, and certificates lacking SAN are rejected with ERR_CERT_COMMON_NAME_INVALID.

Why does my Java app throw SunCertPathBuilderException while cURL works fine? β†’

Java maintains its own independent truststore located at $JAVA_HOME/lib/security/cacerts. Unlike cURL or native applications that read the operating system's truststore (/etc/ssl/certs/ca-certificates.crt), the JVM does not look at the Linux OS certificates unless explicitly directed. You must either import the certificate into Java's cacerts file with keytool, or pass -Djavax.net.ssl.trustStore to your JVM startup command.

What is OCSP Stapling and why should every production server enable it? β†’

Online Certificate Status Protocol (OCSP) allows clients to verify whether a certificate has been revoked before its expiration date. Without stapling, the client's browser must open a separate network connection to the Certificate Authority's OCSP responder server during every handshake, slowing down page loads and exposing user browsing history to the CA. With OCSP Stapling, the web server periodically queries the CA's responder, caches the signed status, and "staples" it directly to the TLS handshake, eliminating latency and privacy leaks.

What is the difference between HTTP-01 and DNS-01 challenges in Let's Encrypt? β†’

In an HTTP-01 challenge, the ACME client provisions a temporary file at http://yourdomain.com/.well-known/acme-challenge/<token> over port 80, proving control of the web server. In a DNS-01 challenge, the client creates a temporary DNS TXT record at _acme-challenge.yourdomain.com via a cloud DNS API (Cloudflare, Route53). DNS-01 is mandatory if you require wildcard certificates (e.g. *.aiagent.org) or are securing internal private servers not exposed to the public internet.