<aside> 🧭

Module 05 · Chain of Trust & Running Your Own CA

In Module 04 you played the CA for one certificate, and every shortcut you took is fixed here. This module explains what a chain actually is, how a client builds one, why the order of certificates in a file matters, and how to run a two-tier private CA you would not be embarrassed to describe in an interview.

🧠 concept → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (answers hidden)

Prerequisite: Modules 01–04. You need basicConstraints and AKI/SKI from Module 03, the CSR and issuing workflow from Module 04, and the verify codes you have been collecting since Module 03 (B2.1).

</aside>


<aside> 📜

The picture to hold in your head for this whole module — the passport, and the office that issued it.

The border officer holds your passport. It was stamped by Regional Office 7, which the officer has never dealt with.

So they ask a second question: who says Regional Office 7 can issue passports? And Office 7 has its own document — a warrant — signed by the national government.

The officer has the government's charter on file. That they trust, because it is on the list.

Three documents, each vouching for the one below it, ending at something already trusted. That is a chain of trust, and everything in this module is about how that stack is built, ordered, transported and constrained.

</aside>

<aside> 🖥️

Set up a working directory before you start.

mkdir -p ~/tls-lab/m05 && cd ~/tls-lab/m05
umask 077
openssl version

Everything here runs offline. You will build a complete two-tier certificate authority from nothing, and the only tools involved are openssl and a text editor.

All expected output in this module was produced on OpenSSL 3.0.13.

</aside>

Part A · What a chain is, and how a client builds one

A1 · The chain, and the checks at every link

<aside> 📖

Official docs: RFC 5280 §6 — Certification Path Validation · openssl verify manual · RFC 5280 §4.1.2.4 — Issuer

</aside>

<aside> 🔗

The analogy — three documents on the officer's desk.

  1. Your passport — says who you are. Stamped by Regional Office 7.
  2. Office 7's warrant — says Office 7 may issue passports. Signed by the government.
  3. The government's charter — signed by nobody but itself, and already on the officer's list.

The officer checks each document against the one above it, and stops when they reach something already trusted. If any link is missing, expired, or not actually authorised to issue, the whole stack fails — no matter how perfect your passport is.

</aside>

A chain is not a special file format. It is just a sequence of certificates where each one's Issuer matches the next one's Subject, ending at a certificate in the client's trust store.

flowchart TD
    L["🍃 LEAF<br>subject: app.internal.test<br>issuer: Issuing CA 1<br>CA:FALSE"]
    I["🏢 INTERMEDIATE<br>subject: Issuing CA 1<br>issuer: Root CA<br>CA:TRUE pathlen:0"]
    R["👑 ROOT<br>subject: Root CA<br>issuer: Root CA<br>CA:TRUE · self-signed"]
    T["💻 TRUST STORE<br>the client already<br>has this file"]
    L -->|"my issuer is..."| I
    I -->|"my issuer is..."| R
    R -.->|"is it on the list?"| T
    style L fill:#d5e8d4,stroke:#82b366,stroke-width:2px
    style I fill:#ffe6cc,stroke:#d79b00,stroke-width:2px
    style R fill:#e1d5e7,stroke:#9673a6,stroke-width:2px

At every link, a client performs the same set of checks. RFC 5280 §6 defines them formally; in practice they are:

Check What fails if it is wrong
Signature This certificate really was signed by the key in the one above it
Validity dates Every certificate in the chain must be in date — including the intermediates
Name chaining Issuer of one equals Subject of the next
basicConstraints Everything above the leaf must be CA:TRUE, or the chain is refused
keyUsage Every issuer must carry keyCertSign
pathlen The chain must not be deeper than an issuer permitted (Part C1)
Name constraints The leaf's names must fall inside what each CA above was allowed to issue (Part C2)
Revocation Nothing in the chain has been withdrawn (Module 09)
Hostname Checked on the leaf only, and separately from the chain

<aside> 🔑

The two things people get wrong about this list.

Every certificate must be in date, not just yours. An expired intermediate breaks a perfectly valid leaf, and monitoring that only checks the leaf will not see it coming. This is a genuinely common outage.

Hostname matching is not part of chain validation. It is a separate check, on the leaf only. That is why openssl verify will happily say OK for a certificate belonging to a completely different site — you have to ask for -verify_hostname explicitly, as you saw in Module 03 (B2.1).

</aside>

🧪 Exercise A1.1 — Look at a real chain and find the links

cd ~/tls-lab/m05

openssl s_client -connect letsencrypt.org:443 -servername letsencrypt.org -showcerts </dev/null 2>/dev/null \
  | sed -n '/BEGIN CERT/,/END CERT/p' > le-chain.pem

grep -c 'BEGIN CERTIFICATE' le-chain.pem

openssl crl2pkcs7 -nocrl -certfile le-chain.pem | openssl pkcs7 -print_certs -noout

🎯 Interview questions — The chain