数字证书与 CA 信任链
基于 HTTP 现代标准 · 核于 2026-06
速查
- 加密只解决「不被偷看」,但不能确认对面是谁——若中间人把自己的公钥换上来,你以为在加密给银行,其实在加密给攻击者。证书就是用来**绑定「公钥 ↔ 身份」**的。
- 数字证书 = 公钥 + 身份信息 + CA 数字签名,标准格式是 X.509:核心字段有主体(Subject CN / SAN)、颁发者(Issuer)、有效期(Not Before / Not After)、公钥、序列号、签名与签名算法。
- CA(证书颁发机构)是被公认可信的第三方:它核验申请者确实控制该域名/身份后,用自己的私钥给证书签名作担保。
- 信任链:根 CA → 中间 CA → 站点(叶子)证书,逐级签名。验证时反向逐级验签,一直追到本地信任的根。
- 根证书内置于操作系统 / 浏览器的信任库(Root Store,如 Mozilla、Apple、Microsoft 的根计划),是整条链的信任锚点;根 CA 通常离线,日常签发交给中间 CA。
- 浏览器验证证书四件事:① 签名链可一路验到受信根 ② 在有效期内 ③ 域名匹配(用 SAN,不再看 CN) ④ 未被吊销;任一不过即告警/中断。
- 域名匹配看 SAN:现代浏览器只认
subjectAltName扩展里的域名,CN已被忽略;支持*.example.com这类通配符。 - 吊销应对「私钥泄露 / 误签发」:三种机制——CRL(吊销清单,大而慢)、OCSP(在线逐张查,泄露隐私且增延迟)、OCSP Stapling(服务器代查并把带时间戳的应答「钉」在握手里,最优)。
- 证书透明度(CT):新证书被记入公开的只追加 Merkle 日志,签发时生成 SCT 作为收录凭证;Chrome / Safari / Firefox 已强制要求 CT,用以事后发现误签发。
- 一句话:证书把「难验证的身份」转化为「可机器验证的密码学签名链」,信任最终都收敛到本地那几百张内置根证书。
加密之外:公钥到底是谁的?
上一页讲清了非对称加密——浏览器拿服务器的公钥加密、只有持私钥者能解。但这里藏着一个致命前提:你怎么确定手里这把公钥真的是 bank.com 的,而不是中间人塞过来的?
设想一个主动攻击者坐在链路中间:你向 bank.com 要公钥,他拦截下来、把自己的公钥回给你。你高高兴兴用它加密了密码——结果直接加密给了攻击者。加密本身完好无损,崩的是公钥的归属。
核心问题
非对称加密保证「只有私钥持有者能解密」,却不保证这把公钥属于你以为的那个人。把公钥和真实身份绑定起来、并让这绑定可被第三方验证,正是数字证书要解决的唯一问题。
数字证书:可验证的「公钥身份证」
数字证书把三样东西打包并由权威方背书:公钥 + 身份信息 + CA 的数字签名。业界标准格式是 X.509(v3),一张站点证书的核心字段如下:
| 字段 | 含义 | 要点 |
|---|---|---|
| Subject | 证书主体(被认证方) | 内含通用名 CN(如 bank.com)等;但域名匹配现在看 SAN,不看 CN |
| Subject Alternative Name(SAN) | 该证书覆盖的域名列表 | 现代浏览器只认 SAN;可含多个域名与 *.example.com 通配符 |
| Issuer | 颁发者(签名方 CA 的名称) | 指向上一级证书,是串起信任链的线索 |
| Validity(Not Before / Not After) | 有效期起止 | 过期或尚未生效都判失败;现代证书有效期已大幅缩短(趋向 ≤ 1 年甚至更短) |
| Public Key | 本主体的公钥 | 握手中用它做密钥交换/验签(RSA、ECDSA 等) |
| Serial Number | 颁发者内唯一的序列号 | 吊销时用它定位某张具体证书 |
| Signature + 算法 | CA 用自己私钥对证书内容的签名 | 任何人可用 CA 公钥验签,确认「内容没被篡改、确由该 CA 签发」 |
| Extensions | X.509v3 扩展 | 含 SAN、密钥用途、吊销信息地址、嵌入的 CT 凭证(SCT)等 |
签名 ≠ 加密
CA 的「签名」是对证书内容做哈希后用 CA 私钥加密得到的;验证方用 CA 公钥还原哈希并比对。它不保护机密性(证书本就是公开的),只保护完整性 + 来源真实性——证明「这份公钥-身份绑定,是这个 CA 认可的,且没被改过」。
把这些字段落到实处,用 openssl 读一张真实证书大致是这样(节选):
Certificate:
Subject: CN = bank.com # 主体通用名
Issuer: C = US, O = Let's Encrypt, CN = R3 # 颁发者 = 某中间 CA
Validity:
Not Before: Jun 1 00:00:00 2026 GMT # 生效时间
Not After : Aug 30 23:59:59 2026 GMT # 过期时间(已趋向短周期)
Subject Public Key Info: ... (ECDSA / RSA) # 本主体公钥
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:bank.com, DNS:*.bank.com # 域名匹配只看这里
Signature Algorithm: ecdsa-with-SHA384 # CA 用何种算法签名看 Issuer 是 R3(一个中间 CA)而非根,正说明日常证书都由中间 CA 签发——这就引出了信任链。
CA 与信任链:信任如何逐级传递
单张证书只是「某 CA 说这把公钥属于 bank.com」。可你凭什么信这个 CA?答案是信任链——把信任一级级往上挂,直到一个你本来就信的锚点。
三级角色
- 根 CA(Root):信任的最终源头。其根证书是自签名的(自己给自己签),靠的不是别人背书,而是被预装进信任库这一事实。根 CA 极其敏感,私钥通常离线保管,平时不直接签站点证书。
- 中间 CA(Intermediate):根 CA 签发给它一张「可以再签发」的证书,日常签发工作由它承担。这样根私钥能离线,万一中间 CA 出事也可单独吊销、不动根。可以有多级中间 CA。
- 站点 / 叶子证书(Leaf):由中间 CA 签发给具体域名,就是你网站部署的那张。
[根 CA 证书] 自签名 · 预装在 OS/浏览器信任库 ← 信任锚点
│ 用根私钥签发
▼
[中间 CA 证书] Issuer = 根 CA
│ 用中间私钥签发
▼
[站点证书 bank.com] Issuer = 中间 CA · 含公钥 + SAN服务器要发「整条链」
站点部署时应同时配置叶子证书 + 中间证书(合称证书链 / fullchain),否则部分客户端的信任库里若没有该中间证书,就会因「链断了、追不到根」而验证失败。根证书无需也不应由服务器下发——它本就在客户端本地。(证书在握手中具体如何传输,见下一页。)
根证书:内置的信任锚点
整条链能不能成立,全看链顶那张根证书是否在本地信任库里。各大根计划——Mozilla(Firefox 及众多 Linux)、Apple、Microsoft、Google——各自维护一份受信根 CA 列表,随操作系统/浏览器分发。一个 CA 要想其证书被普遍信任,必须先通过审计、被纳入这些根计划。
这也解释了几类常见现象:企业内网装「自签证书」会报错,是因为它的根不在公共信任库;公司给员工机器手动导入内部根 CA 后,内网 HTTPS 才不报警——本质就是往信任库里加了一个新锚点(这也是企业中间盒能解密流量的原理,详见「中间人攻击」一页)。
浏览器如何验证一张证书
握手时拿到服务器证书(链)后,客户端按下面四步校验,任一步失败就终止连接并告警:
- 构建并验证签名链:从叶子证书的
Issuer找到中间证书,验证「叶子的签名确由该中间 CA 私钥签出」;再用同样方式上溯,直到链顶证书是本地信任库中的受信根。链中任一签名对不上、或追不到受信根 → 失败(典型报错:unknown issuer/self-signed certificate)。 - 检查有效期:当前时间须落在每张证书的
Not Before~Not After之间。过期或时钟异常都会失败(certificate expired)——所以本机时间错乱常导致全站 HTTPS 报错。 - 校验域名匹配:访问的主机名必须命中证书 SAN 中的某个条目(精确或通配符)。对不上 →
common name/SAN mismatch(你访问a.com却拿到b.com的证书时就是它)。 - 检查是否被吊销:通过 CRL / OCSP / OCSP Stapling(见下节)确认证书未在有效期内被提前作废。
链的强度取决于最弱一环
信任链是「与」的关系:根受信、且每级签名都对、且叶子没过期没被吊销、且域名匹配,全部成立才算可信。任何一环被攻破(如某中间 CA 被黑、误签发),都可能签出一张「看似合法」的证书——这正是下面 CT 机制要兜底的场景。
把验证步骤对照成浏览器报错
前端排查 HTTPS 故障时,浏览器的错误码几乎都能对回上面某一步。常见对照(Chrome 的 NET::ERR_CERT_*):
| 报错 / 现象 | 对应失败的验证步 | 常见原因 |
|---|---|---|
ERR_CERT_AUTHORITY_INVALID / unknown issuer | ① 签名链追不到受信根 | 自签证书、内部 CA 未导入、服务器漏配中间证书 |
ERR_CERT_DATE_INVALID / expired | ② 有效期 | 证书过期、未续期,或本机系统时间不准 |
ERR_CERT_COMMON_NAME_INVALID / mismatch | ③ 域名匹配 | 访问的主机名不在 SAN 内、用了不覆盖的子域、证书只配了裸域没配 www |
ERR_CERT_REVOKED | ④ 吊销 | 证书被 CA 提前作废(私钥泄露/误签发后被吊销) |
漏配中间证书最隐蔽
本机/某些浏览器能打开、换台机器或用 curl 却报 unknown issuer,十有八九是只部署了叶子证书、漏了中间证书:碰巧本地信任库缓存过该中间 CA 的环境能补全链、干净环境补不上。部署时务必用 fullchain(叶子 + 中间)。
证书吊销:让「还没到期」的证书提前失效
证书有有效期,但私钥泄露、信息有误、CA 误签发等情况需要在到期前就让它作废。难点在于:证书已经发到全世界,怎么高效地通知「这张别信了」?三种机制各有取舍:
- CRL(Certificate Revocation List,证书吊销列表):CA 定期发布一份「已吊销证书序列号」的清单,客户端下载后比对。问题:清单越积越大、有缓存延迟,实时性和性能都差。
- OCSP(Online Certificate Status Protocol,在线证书状态协议):客户端就单张证书向 CA 的 OCSP 响应器实时查询「有效/已吊销」。问题:① 每次访问都多一次网络往返,增加延迟;② CA 因此能看到「谁在访问哪个站点」,有隐私泄露;③ 响应器宕机时如何处理(硬失败会拖垮可用性,软失败又给了攻击者可乘之机)也是难题。
- OCSP Stapling(OCSP 装订):改由服务器自己定期向 CA 拉取带时间戳和 CA 签名的 OCSP 应答,并在 TLS 握手时把它「钉」给客户端。客户端无需自己联系 CA——消除了额外往返与隐私泄露,是当前推荐做法(通过 TLS
status_request扩展实现)。
吊销之外的兜底:证书透明度(CT)
吊销解决「已知坏证书」,但误签发往往先被滥用、才被发现。证书透明度(Certificate Transparency) 要求每张新证书被记入公开的、只追加的 Merkle 树日志;签发时日志返回一个 SCT(Signed Certificate Timestamp,签名证书时间戳) 作为「已收录」凭证,可经 X.509 扩展嵌入证书、TLS 扩展或 OCSP Stapling 下发。域名持有者借此能主动监控有没有人偷偷为自己的域名签了证书。Chrome、Safari、Firefox 现已强制要求公共信任证书带合规 CT 凭证,否则不予信任。
小结
数字证书把「公钥到底属于谁」这个难题,转化成一条可被机器逐级验证的密码学信任链:站点证书(公钥 + 身份)由中间 CA 签名、中间 CA 由根 CA 签名,而根证书内置于 OS/浏览器信任库充当信任锚点。浏览器验证时逐级验签、查有效期、用 SAN 匹配域名、再查吊销(CRL / OCSP / OCSP Stapling),辅以证书透明度兜底误签发。证书背后的公钥/私钥与签名机制,建立在上一页 对称与非对称加密 之上;而这条链上的证书在连接建立时究竟如何传输、双方又如何据此协商出会话密钥,是下一页 TLS 握手流程 的主题。