Appearance
Lab:TLS 1.2 ECDHE 握手与 CA 信任链验证
回到专题页:TLS 1.2 / ECDHE / CA 信任链所属模块:00 基础知识(Foundations)
语言无关实验,无需 Android / Flutter / Web 宿主工程。两条路径任选:
- Part A(必做,零依赖) :
node ecdhe-forward-secrecy.mjs离线演示 ECDHE 临时密钥协商与前向安全。 - Part B(推荐,需 openssl) :本地起 TLS 服务 +
openssl s_client -tls1_2抓真实握手报文,识别 CipherSuite / ServerKeyExchange 临时密钥,并复现「漏发中间证书 → 校验失败」。
一句话目标
亲手看到两件事:(1) ECDHE 的会话密钥只来自临时 EC 密钥,长期证书私钥泄露也不影响历史会话(前向安全);(2) 证书校验必须落到内置根 CA,漏发中间证书就会握手失败。
Part A — ECDHE 前向安全(Node,可跑)
bash
node ecdhe-forward-secrecy.mjs脚本会:
- 双方各自生成临时 P-256 密钥(用完即弃,不落盘);
- 临时 ECDH 协商出会话密钥,验证两端结果一致;
- 说明长期证书密钥只签名临时参数、不接触会话密钥 —— 这就是前向安全;
- 从该会话密钥用 HKDF 派生 AES-256-GCM 密钥并做加解密往返,呼应「记录层用对称加密」。
预期输出(节选):
2) 临时 ECDH 协商会话密钥 ...
=> 两端一致: true
4) 派生 AES-256-GCM 密钥并加解密往返: OK观察重点:第 2 步两端独立算出的密钥完全一致;第 3 步强调临时私钥握手后即弃,所以长期私钥泄露无法回溯解密历史流量。
Part B — 真实握手抓包与证书链(openssl,离线)
B.1 建一条 CA → 中间 → 叶子 证书链
bash
# 在 lab 目录下建临时工作区
mkdir -p demo && cd demo
# 1) 根 CA(自签,模拟手机/系统内置的可信根)
openssl req -x509 -newkey rsa:2048 -nodes -keyout ca.key -out ca.crt \
-days 3650 -subj "/CN=Lab Root CA"
# 2) 中间 CA(由根签发)
openssl req -newkey rsa:2048 -nodes -keyout inter.key -out inter.csr \
-subj "/CN=Lab Intermediate CA"
openssl x509 -req -in inter.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out inter.crt -days 1825
# 3) 叶子(站点证书,由中间签发,带 SAN)
openssl req -newkey rsa:2048 -nodes -keyout leaf.key -out leaf.csr \
-subj "/CN=example.com"
printf "subjectAltName=DNS:example.com\n" > ext.cnf
openssl x509 -req -in leaf.csr -CA inter.crt -CAkey inter.key -CAcreateserial \
-out leaf.crt -days 825 -extfile ext.cnfB.2 起本地 TLS 服务(完整链:叶子 + 中间)
bash
# 服务端证书链 = 叶子 + 中间(顺序:leaf 在上,中间在下)
cat leaf.crt inter.crt > chain.pem
openssl s_server -accept 8443 -cert chain.pem -key leaf.key \
-cert_chain inter.crt -tls1_2 -www另开一个终端,用 s_client 抓握手(强制 TLS 1.2 + ECDHE):
bash
openssl s_client -connect localhost:8443 -tls1_2 -cipher ECDHE-RSA-AES128-GCM-SHA256观察重点(在 s_client 输出里找):
Cipher : ECDHE-RSA-AES128-GCM-SHA256—— CipherSuite:ECDHE负责密钥交换、RSA负责证书签名、AES128-GCM负责记录层加密。Server Temp Key: ECDH, P-256—— 这就是临时 EC 公钥(ServerKeyExchange 报文),每次握手都不同。- 证书块列出
example.com(叶子)→Lab Intermediate CA→Lab Root CA(根),链完整。
B.3 复现「漏发中间证书 → 校验失败」
bash
# 只发叶子,不发中间(很多「浏览器能连、App 连不上」就是这个坑)
openssl s_server -accept 8444 -cert leaf.crt -key leaf.key -tls1_2 -wwwbash
# 用根 CA 作为信任锚去校验
openssl s_client -connect localhost:8444 -tls1_2 -CAfile ca.crt
# => verify error:num=21:unable to verify the first certificate现象解释:客户端手里只有「叶子」和「内置根」,中间的跳断了,无法用根公钥直接验证叶子(叶子是中间签的)。补上中间证书(回到 B.2 的 chain.pem)即恢复 Verify return code: 0 (ok)。
B.4 移动端抓取提示(呼应专题页问题 3)
浏览器默认信任系统根池,安装用户 CA 即可 MITM;而 Android 7+ 默认不信任用户 CA、iOS 需装描述文件并手动信任,且 App 还要在网络安全性配置里显式信任用户 CA —— 所以同一代理在手机上常「只能看到加密隧道」。
对应专题页知识点
| 本实验看到的现象 | 专题页小节 |
|---|---|
ECDHE-* CipherSuite + Server Temp Key | 底层机制 · ECDHE 密钥交换 |
| 临时密钥每次不同、长期私钥只签名 | 复习检查题 1(前向安全) |
漏发中间 → num=21 校验失败 | 复习检查题 2(信任链) |
| 证书块 叶子←中间←根 | 速记 · 信任链 |
常见问题
s_client连不上:确认s_server还在跑、端口未被占用;本实验全部localhost,无需联网。unknown option -cert_chain:说明是 LibreSSL(macOS 系统自带),换成完整 OpenSSL 3.x(本机openssl version显示OpenSSL 3.6.2即可正常用-cert_chain);或直接用 B.2 的chain.pem(已含叶子+中间)配-cert chain.pem即可,无需-cert_chain。- Part A 报错:Node ≥ 15 才有
X509Certificate/webcrypto派生 API;本机 Node 22 可直接跑。