The TLS Certificate Chain Explained (Root, Intermediate, and Server Certificates)

Cryptography for IoT Hackers - Part 2 of 13

Series Index

Part Title Status
1 What TLS Actually Does (And Why Your IoT Device Needs It) :white_check_mark: Published
2 - You are here Certificates From Scratch The Trust Chain Nobody Explains :white_check_mark: Published
3 Setting Up Your Own CA and Issuing Certs with OpenSSL :white_check_mark: Published
4 ESP32 + TLS: Your First Secure MQTT Connection :white_check_mark: Published
5 What the TLS Handshake Looks Like on the Wire (Wireshark Lab) :white_check_mark: Published
6 Mutual TLS (mTLS) Explained: When the Server Also Verifies the Client :white_check_mark: Published
7 MITM Attack on a TLS IoT Device - What Breaks and What Doesn't :white_check_mark: Published
8 Cert Pinning The Fix, and How Attackers Bypass It :soon_arrow:
9 Embedded Crypto Pitfalls: Hardcoded Keys and Weak RNG :soon_arrow:
10 Breaking mTLS: Stolen Certs and Certificate Confusion :soon_arrow:
11 Secure Provisioning How to Get Certs Onto Devices Safely :soon_arrow:
12 Building a Hardened ESP32 TLS Client Checklist and Final Lab :soon_arrow:
13 Secure OTA Updates Why Your Update Channel Is an Attack Surface :soon_arrow:

Before We Start

In Part 1, I explained what TLS does at a big picture level. TLS encrypts data in transit, verifies the server’s identity using certificates, and without it your ESP32 is wide open to MITM attacks.

But I realized after writing Part 1 that I had skipped something important.

I kept saying “the ESP32 checks the certificate” but I never explained what a certificate actually is. What is inside it? Who made it? Why does the ESP32 trust it? How does this whole thing not fall apart?

That is what this part is about.

And fair warning when I first tried to understand this stuff, I got confused multiple times. I am going to show you exactly where I got confused and how it clicked for me. Because if you are reading this blog, you are probably at the same level I was. And I think that is actually useful.

Let’s go.


What Is a Certificate, Really?

Here is the first question I want you to think about before we get technical.

You have a passport. What information is printed on it?

Think about it. Name. Date of birth. Country of citizenship. Address. Expiry date. And most importantly a stamp and signature from your government.

Now here is the key thing about that passport. When you land at a foreign airport, the border officer does not know you personally. They have never met you. But they trust your passport. Why? Because they trust the government that issued it. Nepal stamped it. The officer knows what Nepal’s stamp looks like. So they trust you.

A TLS certificate works exactly the same way.

A TLS certificate is basically a digital passport for a server.

When your ESP32 connects to a server, it does not know that server personally. It has never met it. But the server shows a certificate a digital document that says “I am api.example.com, and a trusted authority has verified this.”

The ESP32 checks that certificate the same way the border officer checks your passport — by verifying the authority that issued it.


What Is Actually Inside a Certificate?

Just like a passport has specific fields printed on it, a certificate has specific fields too.

Here is what is inside every TLS certificate:

Common Name (CN) - This is the domain name the certificate belongs to. For example: api.example.com or *.google.com. This is the “name” field, like your name on your passport.

Issuer - Who issued this certificate. This is the Certificate Authority (CA) like DigiCert or Let’s Encrypt. Just like your passport says “Government of Nepal” issued it.

Validity dates - Valid From and Valid Until. Certificates expire, just like passports. An expired certificate gets rejected.

Public Key - This is the extra field that a passport does not have. It is a mathematical key that belongs to the server. We will talk about this in a minute.

CA’s Digital Signature - This is the stamp. The CA has signed this certificate using their private key, proving they verified it. This is the most important field.

One thing I want to be very clear about - the private key is never inside the certificate. The private key stays secret with the server owner. Always. If anyone ever tells you the private key is in the certificate, they are wrong.


Okay But What Is a Public/Private Key Pair?

This confused me a lot at first because it sounds very mathematical. But it is actually a simple concept.

Imagine a padlock that anyone in the world can close but only one specific person has the key to open it.

That is basically what a public/private key pair is.

  • Private key - only the owner has this. It is secret. Never shared.
  • Public key - can be given to anyone. It is meant to be shared.

Now here is where it gets interesting. For digital signatures (which is what we care about for certificates), the keys work like this:

  • Private key is used to sign something
  • Public key is used to verify that signature

So if a CA signs a certificate with their private key, anyone who has the CA’s public key can verify that signature. They can confirm “yes, the real CA signed this.”

Think of it like Nepal’s official government stamp. Nepal uses their official stamp (private key) to stamp your passport. A border officer knows what Nepal’s official stamp looks like (public key). They can verify it is real but they cannot make a fake stamp themselves.


So What Is the CA Actually Doing When It Signs a Certificate?

Let me walk through this step by step because this is where I had to slow down.

Say I own the domain a.com. I want a TLS certificate for it.

I go to a CA let’s say DigiCert.

DigiCert says: “Prove you own a.com.”

I prove it through domain verification, documentation, whatever they require.

DigiCert verifies me. Then they take my certificate (which contains my domain name, my public key, validity dates etc.) and they sign it with their private key.

Now that certificate has DigiCert’s signature on it.

When your ESP32 connects to my server, my server sends that certificate. Your ESP32 checks the signature using DigiCert’s public key which you already stored on the ESP32 before deployment.

If the signature checks out ESP32 says “this server is legitimate, the CA verified it.”

If the signature does not check out ESP32 rejects the connection.

This is why in Part 1 you saw code like this:

client.setCACert(root_ca);

That root_ca string you paste into your code that is the CA’s certificate. And inside that certificate is the CA’s public key. You are pre-loading the CA’s public key onto your ESP32 so it can verify server certificates later.


Why Does Chrome Have Hundreds of CAs But ESP32 Has Only One?

This is a great question and it confused me too.

When you open Google Chrome, it comes pre-installed with hundreds of trusted CA certificates. This is called the trust store. Your operating system has it too. Windows, macOS, Ubuntu all of them come with a list of CAs that are considered globally trusted.

So when you visit google.com, Chrome checks the certificate against its built-in list of trusted CAs. It finds a match. Done.

But ESP32 is not a laptop. It does not come with a trust store. It has almost no storage. It has no built-in list of trusted CAs.

So you have to tell it manually “trust this specific CA, and only this CA.”

That is why you flash the CA certificate into your firmware yourself. You are building a tiny trust store with exactly one entry.

This also makes ESP32 more secure in some ways it only trusts the one CA you told it to. It cannot be tricked by any other CA.


The Trust Chain - The Part That Confused Me The Most

Okay. This is the part where I got stuck for a while. Let me be honest about that.

I kept hearing “Root CA, Intermediate CA, chain of trust” and I did not understand why there were three levels. Why not just one CA signing everything?

Let me explain it the way it finally clicked for me.

The King and the Minister Analogy

Imagine you are the most important judge in Nepal. Your signature on any document is the final word. Every court in the country and every country in the world trusts your signature.

Now do you sit in a busy public office where anyone can walk in and hand you papers to sign?

No. You sit in a protected private chamber. Because if someone steals your official stamp, game over. They can sign anything they want and everyone will trust it.

So instead, you appoint a trusted minister. The minister sits in the public office and does the day-to-day signing. If the minister gets robbed that is bad, but it is not game over. You can cancel the minister’s authority and appoint a new one. Your stamp is still safe.

That is exactly how the certificate trust chain works.

Root CA (offline, locked in a vault — the Judge)
  └── Intermediate CA (online, does daily signing - the Minister)
        └── Server Certificate (your a.com cert - the document being signed)

Root CA - This is the most trusted entity in the system. Its private key is kept on an air-gapped machine (never connected to the internet), physically locked in a secure facility. It only comes online to sign Intermediate CA certificates then goes offline again.

Intermediate CA - This is the one that does the actual work. It is online and signs server certificates for domains like yours every day. If it gets compromised, Root CA can revoke it.

Server Certificate - This is what your ESP32 sees. Signed by the Intermediate CA.

What Happens If the Intermediate CA Gets Hacked?

Let me give you a real attack scenario because this is where it clicked for me.

Say a hacker breaks into DigiCert’s Intermediate CA server and steals its private key.

Now the attacker can sign a fake certificate for google.com and it will look completely legitimate. Every browser will trust it because the chain traces back to DigiCert’s Root CA.

What happens next? The attacker sets up a fake server pretending to be google.com. When you type google.com in your browser, the attacker intercepts your request and sends you to their fake server. Your browser checks the certificate the signature is valid, the chain is intact and shows you the green padlock.

You think you are on real Google. You type your password. The attacker reads everything.

This is a full MITM attack. And your browser had no way to detect it because the certificate was signed with a real key.

But here is the important part. DigiCert still has their Root CA safely offline, untouched. They can revoke the compromised Intermediate CA and issue a new one. The damage is contained.

Now imagine there was no Intermediate CA. The Root CA was online, signing everything directly. Attacker steals the Root CA private key. There is nothing above the Root CA to revoke it. The entire TLS system is broken. Every device on earth is compromised.

That is why three levels exist.


How Does ESP32 Verify the Full Chain?

When your ESP32 connects to a server, the server does not just send its own certificate. It sends the full chain:

  1. The server certificate (signed by Intermediate CA)
  2. The Intermediate CA certificate (signed by Root CA)

ESP32 then does this:

Step 1 - Check server certificate
         Is it signed by the Intermediate CA? ✅

Step 2 - Check Intermediate CA certificate  
         Is it signed by the Root CA? ✅

Step 3 - Check Root CA
         Is this the CA I have stored in my firmware? ✅

Result - TRUSTED. Connection proceeds.

If any step fails expired cert, wrong signature, unknown CA connection is rejected.


Self-Signed Certificates Why They Are Dangerous

A self-signed certificate is exactly what it sounds like a certificate that is signed by itself, not by any CA.

Anyone can make one. I can generate a self-signed certificate for google.com right now on my laptop. It takes about 30 seconds.

Here is the problem. When ESP32 tries to verify it it traces the chain upward. But there is no chain. The certificate signed itself. There is no trusted authority above it.

So if an attacker creates a fake self-signed certificate claiming to be your server, ESP32 has no way to tell the difference between your legitimate self-signed cert and the attacker’s fake one.

This is fine for local testing and development you know what you are connecting to. But in production, on a deployed device, it is dangerous. If ESP32 is configured to blindly trust self-signed certificates, any attacker can MITM the connection.

Always use a properly CA-signed certificate in production.


Quick Summary - What We Covered

Let me put it all together before we move to Part 3.

A TLS certificate is like a digital passport for a server. It contains the domain name, who issued it, validity dates, the server’s public key, and the CA’s digital signature.

The CA signs the certificate with its private key. Anyone can verify that signature using the CA’s public key which you store on your ESP32 before deployment.

The trust chain exists in three levels because the Root CA’s private key is too important to put online. If it gets stolen, the entire internet’s TLS system breaks. So Root CA stays offline, Intermediate CA does the daily work, and if Intermediate CA is compromised, Root CA can revoke it and recover.

ESP32 verifies the full chain from the server certificate, up through the Intermediate CA, all the way to the Root CA you stored in firmware.

Self-signed certificates have no chain no trusted authority verified them. Fine for testing, dangerous in production.


What’s Coming in Part 3

Now that you understand what certificates are and how the trust chain works in Part 3 we are going to get our hands dirty.

We will use OpenSSL on Ubuntu to create our own CA from scratch, issue certificates, and inspect what is actually inside them. You will see every field we talked about in this part with real commands and real output.

If this part was theory Part 3 is where it becomes real.