公钥加密解决了"怎么送钥匙",但没解决"怎么确认这把钥匙是它自称的那个人"——数字证书、证书颁发机构、PKI、根证书这几样东西,全都是为了补上这一个缺口。
5-3 把公钥公开出去了,但"公开"这件事本身留了一个洞:攻击者同样可以公开一把假公钥。
这一节就是来堵这个洞的,而且它用的手段你已经见过——还是签名,只是这次签的不是数据,而是"某把公钥属于某个人"这句话。它也是 冒充特定的人物 那类攻击在密码学层面的正面回应。
一、光有公钥不够:交换过程中的那个空档
假设 A 想把公钥发给 B。如果这个过程没有人干扰,一切正常。但如果有人站在中间:
- A 发出自己的公钥;
- 攻击者截下来,换成自己的公钥,再转发给 B;
- B 拿到的是攻击者的公钥,以为那是 A 的,于是用它加密消息;
- 攻击者用自己的私钥解开,读完之后再用 A 的真公钥加密转发给 A。
双方全程看不到任何异常——这就是"公钥替换",也是所有中间人攻击的理论原型(5-14 会展开)。
问题的根子在:公钥只是一个字符串,它自己没法证明"我属于某某"。要证明这一点,得有另一个公钥不算在内的人出来签字担保——于是出现了 CA。
二、数字证书:一张签过名的名片
数字证书(digital certificate)的本质非常朴素:
它是一份结构化的文本,里面写着【某主体的公钥 + 主体信息 + 有效期 + 用途】,然后由证书颁发机构(CA)用自己的私钥签了名。
它的作用:任何人拿到这张证书,先用 CA 的公钥验证签名——验过,就说明这份内容确实是 CA 认可的、中途没被改过。因为 CA 的私钥只有 CA 有,别人伪造不了这张名片。
几个名词就是这套体系的不同切片:
| 名词 | 指的是什么 |
|---|---|
| 数字证书 | 那份"签过名的名片"本身 |
| 证书颁发机构(CA) | 负责核实并签发证书的机构 |
| PKI(公钥基础设施) | 整套体系:CA + 证书格式 + 签发与吊销流程 + 客户端校验规则 |
| 根证书 | 信任链的起点,是预置在你系统里的那批 CA 证书 |
| 服务器证书 | 网站用的那一类证书(绑定域名) |
三、动手 Demo:一条证书链的验证与篡改
证书不是一张,而是一条链。Demo 用三级链模拟了从根到服务器的验证过程:
# 用"主体的名字 + 签发者的密钥"模拟一次签名
def sig(name, key):
return hashlib.sha256((name + "|" + str(key)).encode()).hexdigest()[:20]
root = {"sub": "Global Root CA", "key": 7001} # 根:自签
inter = {"sub": "Global Secure Server CA", "key": 1103} # 中间:由根签
leaf = {"sub": "www.example.com", "key": 1307} # 服务器:由中间签
# 验证:用【上一级的公钥】重算下一级的签名,看是否一致
sig(inter["sub"], root["key"]) == inter_sig
sig(leaf["sub"], inter["key"]) == leaf_sig
实跑输出:
一、一条证书链就是三级【签过名的名片】
层级 证书主体 由谁签的
① 根证书 Global Root CA 自己签自己(预置在系统里)
② 中间证书 Global Secure Server CA Global Root CA
③ 服务器证书 www.example.com Global Secure Server CA
三张名片上的签名(模拟):
根证书签名 f5a5d87e67073a9d0a73
中间证书签名 f32e7d2bdac5a20671f9
服务器证书签名 87562b3dae0e50c0a658
二、浏览器怎么验(自下而上,一路验到根)
① 用【预置的根公钥】重算中间证书的签名:f32e7d2bdac5a20671f9 → 一致
② 用【中间证书的公钥】重算服务器证书的签名:87562b3dae0e50c0a658 → 一致
③ 域名是否匹配、是否在有效期内、是否被吊销 → 全过才显示挂锁
三、改一个字符会怎样(形近域名钓鱼)
原来的域名 www.example.com
改成 www.examp1e.com (字母 l 换成数字 1)
重算签名 80eaf09f824dca72d2fc
原签名 87562b3dae0e50c0a658
→ 不一致,浏览器拒绝
证书挡住了【域名被改】这件事,但挡不住
【攻击者真的注册了这个形近域名、再合法申请一张证书】。
四、自签证书:没有第三方背书,就是自己给自己发名片
主体 = www.my-lab.local,签发者 = 也是 www.my-lab.local(自己)
签名 = bc2c4c2273c2d1833d7b
→ 校验技术上能过,但浏览器找不到【信任链的起点】,
于是红警告:不是私密连接。警告页上那个【继续】不该点。
五、根证书是整套体系的信任起点,也是它最脆弱的地方
· 浏览器 / 操作系统预置约 100~150 个根证书,签一次就是全网信任
· 2011 年荷兰 CA DigiNotar 被入侵,签发伪造证书对伊朗用户做中间人,
事后被所有浏览器吊销、公司破产——一次失守,整个根一起作废
· 2026 年正在收紧的规则:公开信任的 TLS 证书最长有效期
2026-03-15 起 200 天 → 2027-03-15 起 100 天 → 2029-03-15 起 47 天
读这个输出:
- 第二段是整节的核心:验证的动作是"自下而上"的——从服务器证书开始,用上一级的公钥去重算下一级的签名,一直验到浏览器内置的根证书。只要链条中间有任何一环对不上,整条链就断了。
- 第三段划出了证书能力的边界:它能证明"这个域名没被改",但不能证明"这个域名背后是好人"。
www.examp1e.com(字母 l 换成数字 1)是攻击者自己注册并合法申请的——它有一张完全合法、验证全过的证书。 这一条非常重要,它直接说明了后面 5-8 为什么"看挂锁"这个习惯是错的。 - 第五段的 DigiNotar 事件值得记住年份(2011):一个 CA 被攻破,理论上可以让全世界所有网站的"可信身份"同时失效。这就是为什么"根证书"是整个 PKI 里最需要盯紧的一环(也是后面 5-10 里"证书透明度"要解决的问题)。
四、⚠️ 2026 现状
- 【证书有效期正在被大幅压缩,这是这两年最大的规则变化】。 CA/Browser Forum 通过的规则(SC-081v3,2025 年 4 月表决通过)把公开信任的 TLS 证书最长有效期分三步压缩:2026-03-15 起 200 天 → 2027-03-15 起 100 天 → 2029-03-15 起 47 天。同时"域名验证信息可以复用多久"也在收紧:2029 年起只剩 10 天。 这个变化背后的理由很直接:证书里记录的信息会随时间变得不准,而吊销机制又不可靠——那就干脆让它快速过期。
- 【缩短有效期的真正后果是"自动化不再是可选项"】。 按公开的估算,100 张证书在 47 天周期下大约是每年 800 次续期;如果每次手工处理按 2 小时算,就是每年约 1600 个工时——这已经不是"麻烦",而是一个专职工位。 所以 ACME 这类自动签发协议今天已是基础设施(5-8 会讲证书自动化与部署)。
- 【免费证书让"有证书"这件事彻底普及,也让它失去了鉴别力】。 Let's Encrypt 这类 CA 免费、秒级自动签发,只验证"你控制这个域名"——这对整个互联网是好事(加密变成默认),但也意味着"有证书"再也没有"这是个可信机构"的含义。钓鱼网站几乎都有证书,这是 2026 年最需要向普通人纠正的一条。
- 【根证书的信任面也在被重新审视】。 浏览器与操作系统预置的根证书大约在 100~150 个量级,签一次就获得全网信任——这意味着"任何一家 CA 失守"都是全行业的风险。这也是为什么证书透明度(CT)日志被主流浏览器强制要求:它让所有签发行为都留下公开记录,事后可查(详见 5-10)。
30 秒实验
今天做两个动作,各 15 秒:
- 在浏览器里点开任意网站的证书信息,找到"有效期"那一栏:看它还剩几天。如果这个网站的有效期只剩十几天甚至几天,说明它背后有自动续期在跑——这就是 47 天时代的标准工作方式。
- 找一个你自己搭建或见过的自签证书场景(比如路由器管理页、内网设备页),故意访问一次,看清那个红警告页。然后问自己一句:如果我顺手点了"继续",我实际上放弃了什么?——答案:我放弃了"确认对面是它自称的那台机器"这件事,从此中间人换掉公钥我也看不出来。
这一节要带走的一句是:公钥加密给了每个人一把锁,数字证书解决的是"我怎么知道这把锁真是他的"——它靠的不是技术更聪明,而是让一个双方都信的第三方在锁上刻了自己的名字。
常见误区
-
错误:有证书就说明这个网站是安全的、可以放心输入密码。——纠正:证书只能证明两件事:这个域名确实归证书持有者控制、以及传输过程没被改过。它证明不了网站是好人、也证明不了网站会妥善处理你的数据。2026 年的现实是,绝大多数钓鱼网站都有合法的证书——因为"证明你控制这个域名"只需要几十秒、不花钱、不查身份。真正该看的是完整的域名,而不是有没有锁(5-8)。
-
错误:中间的"中间证书"是多余的,根证书直接签服务器证书不就行了。——纠正:中间证书的存在是为了保护根证书。根证书的私钥是整个体系的命门(它一失守,所有签发的证书全部失效,DigiNotar 就是先例),所以它必须离线保管、极少使用。做法就是让根只签一批中间 CA,日常签发全部由中间 CA 完成——万一中间 CA 出事,可以只吊销它一个分支,根仍然安全。 一句话:分层不是为了好看,是为了"出事时能局部止损"。
-
错误:证书有效期缩短纯粹是 CA 想多收钱。——纠正:这是浏览器厂商主导的规则,而且官方给出的理由和钱无关。CA/Browser Forum 的规则变更(SC-081v3)由 Apple 提出、Google/Mozilla/Microsoft 全票支持,主要理由是两条:① 吊销机制不可靠(软失败导致浏览器在验证不了时照样放行),缩短有效期可以让"吊销失败"这件事的影响窗口天然变小;② 为后量子迁移做铺垫——能快速轮换证书的组织,才能在未来算法必须更换时跟得上。而且 CA 明确说过不会因此加价(按年订阅,不按张数)。真正的成本落在使用方:手工续期在 47 天周期下不可行,必须上自动化。
-
错误:证书被吊销之后,网站立刻就打不开了。——纠正:不一定,而且"不一定"本身才是问题。吊销信息要通过 CRL 或 OCSP 传播,而浏览器在查不到吊销信息时通常选择"放行"——这个行为叫软失败(soft-fail),原因是"网络一抖就让全网不能访问"代价太大。结果就是:一张已被吊销的证书,现实中可能仍然能正常使用一段时间。这直接解释了为什么业界最后选择了"缩短有效期"这条路——与其指望吊销能及时生效,不如让证书本来就活不长(完整的机制对比见 5-10)。
自测三问(点开对答案)
1. 为什么"公开公钥"这个动作本身不安全? —— **因为公钥只是一个字符串,它不能自证身份**。**攻击者可以在传输过程中把它替换成自己的公钥**,于是**你以为在用对方的锁,实际上用的是攻击者的锁**——**对方的私钥解不开你发的消息,但攻击者的能解开**。**更麻烦的是双方都察觉不到**:A 以为消息安全送到了 B,B 以为自己在和 A 通信。**这个问题的本质是"缺少一个双方都认可的第三方来担保"**——**所以解法不是"把公钥藏起来"(公钥本来就要公开),而是"让第三方在公钥上签名"**,这就是数字证书(本节)。 2. 为什么证书链是"三级"而不是"根直接签服务器"? —— **为了保护根证书的私钥**。**根私钥一旦泄露或被滥用,所有由它签发的证书全部失效**——**2011 年 DigiNotar 事件就是这么收场的:一家 CA 被入侵,它签出的伪造证书直接让整个根在浏览器里被吊销、公司破产。** **分层的做法是:根离线保管、只签少量中间 CA;日常签发全部由中间 CA 承担。** **好处是"故障可隔离"**:**中间 CA 出事只需吊销那个分支,根本身不动。** **这也是各类 PKI 体系(不止 TLS,还有企业内部 PKI、代码签名)共同的结构**。 3. 证书能挡住"形近域名钓鱼"吗? —— **挡不住**。**`www.examp1e.com`(字母 l 换成数字 1)这类域名,攻击者可以自己注册、然后合法申请一张证书——校验全部通过,锁照常显示**。**证书能挡的是"把你原本要访问的域名悄悄改成别的"**(比如在中间替换证书内容),**挡不住"攻击者真的拥有一个长得像的域名"。** **所以防范形近域名靠的是另一套东西**:**看清完整域名、用密码管理器(它只在域名完全匹配时自动填充)、收藏官方入口而不是点邮件链接**(3-4 讲的就是这个场景)。- ☐ 下一节(5-5 防止数据被篡改的技术)
相关笔记
- 解决密钥分发问题的密码 —— 上一篇:把公钥公开出去
- 防止数据被篡改的技术 —— 下一篇:证书里的签名,靠什么保证内容没被改
- 冒充特定的人物 —— 冒充这件事,证书是从哪个角度堵的
- 利用假冒网站发起的攻击 —— 形近域名钓鱼:证书挡不住的那一类
- 🛡️ 网络与信息安全 · 索引 —— 全课路线图