证书解决的是「这把公钥属于谁」。它把公钥和域名绑在一起,再由一个受信的机构盖章。 验证时客户端顺着签发关系一路追到受信根,追不到就不认。
上一节有了钥匙,这一节解决「怎么确认钥匙的主人」。没有这一步,前面所有加密都可以对着中间人做。
一、证书里到底有什么
Demo 用 openssl 现场造了两张:一张自签的根 CN=Demo Root CA,
一张由它签发的服务器证书 CN=demo.local。
看四个字段就能读出关系:服务器证书的 Issuer 正好等于根证书的 Subject;
根证书的 Issuer 等于自己的 Subject(所以叫自签);
两张的签名算法都是 sha256WithRSAEncryption,公钥算法都是 rsaEncryption。
真实证书还多两样东西:SAN(这个证书对哪些域名有效)和有效期。
SAN 是现代校验的真正依据,CN 早就只作参考了——所以只写 CN 不写 SAN 的证书,浏览器会直接拒。
二、链是怎么验的
客户端拿到服务器证书,先看 Issuer 是谁,再去自己的信任库里找那张证书;
找到之后用它的公钥验签名,验过了就再看下一层,直到碰到一个「本来就在信任库里」的根。
Demo 里两组对照:信任 ca.pem 时通过,收到 103 字节;
不信任时被拒,报错是 unable to get local issuer certificate——意思是「链追到一半断了,
找不到签发者」。
注意这个报错和 7.1.2 里的 Hostname mismatch 是两码事。一个是链断了,一个是名字不对。
排查证书问题时先分清是哪一种,能省很多时间。
三、现实里的证书从哪来
浏览器和操作系统内置了一百多个根证书。 正规站点不需要你手动加根,它的链能追到某个内置根上——中间可能还有一两层中间证书。 签发证书是要花钱的,直到 2015 年 Let's Encrypt 出现,把「域名验证型」证书做到了免费、自动化、 90 天一期。这件事直接推动了全网的 HTTPS 普及率从一半涨到九成以上。 自签证书并没有消失,它在内网、测试、物联网设备里大量使用——代价是每台客户端都得手动信任它。 这也解释了为什么「请安装我们的根证书」这种要求在企業内网很常见,在公网就很可疑。
四、动手 Demo:造一条两级链,看信任库起什么作用
用 openssl 生成一个自签根和一个由它签发的服务器证书, 打印两张证书的 Subject/Issuer/序列号/签名算法; 然后起一个真 TLS 服务端,客户端分别带着和不带根证书去连。
# 1. 自签根
openssl req -x509 -newkey rsa:2048 -keyout ca.key -out ca.pem \
-days 2 -nodes -subj '/CN=Demo Root CA' \
-addext 'basicConstraints=critical,CA:TRUE'
# 2. 服务器证书,由根签发
openssl req -newkey rsa:2048 -keyout srv.key -out srv.csr -nodes \
-subj '/CN=demo.local'
openssl x509 -req -in srv.csr -CA ca.pem -CAkey ca.key \
-CAcreateserial -out srv.pem -days 2 -extfile srv.cnf
# 3. 客户端只做一件事:决定信任库里放不放那张根
ctx.load_verify_locations(ca_pem)
实跑输出:
--------------------------------------------------------------------
证书:把公钥和名字绑在一起,再盖个章
--------------------------------------------------------------------
先用 openssl 现场造一条两级链:一个自己当根、一个由它签发。
根证书 ca.pem:
subject=CN=Demo Root CA
issuer=CN=Demo Root CA
serial=74F7A6B3EEA0A07939B056CD4908F82142915896
notBefore=Oct 25 06:50:40 2026 GMT
notAfter=Oct 27 06:50:40 2026 GMT
Signature Algorithm: sha256WithRSAEncryption
Public Key Algorithm: rsaEncryption
服务器证书 srv.pem:
subject=CN=demo.local
issuer=CN=Demo Root CA
serial=4BC4AD268A2096D9E53F29E8E8A2353235D95A35
notBefore=Oct 25 06:50:40 2026 GMT
notAfter=Oct 27 06:50:40 2026 GMT
Signature Algorithm: sha256WithRSAEncryption
Public Key Algorithm: rsaEncryption
两份证书的关系,靠这三个字段就能看出来:
srv.pem 的 Issuer = ca.pem 的 Subject
srv.pem 上带 CA 的签名,用 ca.pem 的公钥能验过
ca.pem 是自签的(Issuer 等于 Subject),所以它必须出现在信任库里
客户端手里有 / 没有那张根证书,结果完全不同:
信任 ca.pem 通过,收到 103 字节
不信任 ca.pem 拒绝:unable to get local issuer certificate
第一条通过,是因为客户端把 ca.pem 放进了信任库,
于是 `srv.pem → ca.pem` 这条链能追到底,签名也验得过。
第二条拒绝,因为链断在了半路——`srv.pem` 的签发者不在信任库里。
这就是证书的作用:**它不是加密用的,是用来证明「这把公钥属于这个名字」**。
没有它,公钥交换就只能靠「我相信第一个给我钥匙的人」,
而那正是中间人最擅长的场景。
再补一句现实情况:浏览器内置了一百多个根证书,
所以正规站点不用你手动加根——它的链能追到某个内置根上。
根证书:Subject 和 Issuer 都是 CN=Demo Root CA,序列号一长串十六进制。
服务器证书:Subject CN=demo.local,Issuer CN=Demo Root CA——这两个字段对上了,链就连起来了。
信任根时通过,收到 103 字节;不信任时被拒,报错 unable to get local issuer certificate。
同一对密钥、同一张证书,只因为客户端信任库不同,结果就完全相反。
这说明「合法」不是证书自身的属性,而是证书加信任库共同决定的。
30 秒实验
- 把
-days 2改成-days 0,看报错变成什么。 - 给服务器证书加一个 SAN 为
other.local,用demo.local去连,看是否被拒。 - 想一想:为什么企业内网要求你安装根证书时,要格外小心?
常见误区
- 「证书是用来加密的」——它用来证明公钥归属,加密由协商出的会话密钥做。
- 「CN 写了域名就够了」——现代校验看 SAN,只写 CN 会被拒。
- 「自签证书不能用」——内网、测试、设备里大量使用,只是要手动信任。
自测三问(点开对答案)
1. 怎么从字段上判断两张证书是一对签发关系? —— 子证书的 Issuer 等于父证书的 Subject。 2. `unable to get local issuer certificate` 是什么意思? —— 链断了:签发者不在客户端的信任库里。 3. 证书本身是靠什么保证不被伪造的? —— 签发者的签名。要伪造就得先攻破签发者的私钥。材料齐了:三样保护、外壳位置、两把钥匙、证书。最后把它们串成一次完整握手。
- ☐ (本节完,下一节 7.2.5 HTTPS的安全通信机制)