一家 CA 给服务器签发了一张证书。我们能正常解析它,证书里的签名也能被颁发者公钥验证。现在可以放心建立连接了吗?
还不行。
签名正确只说明证书内容没有在签发后被修改,而且某个私钥曾经为它签名。客户端仍要回答:这个颁发者是否能一路连接到自己认可的信任锚?链上的 CA 是否有权签发这类证书?证书是否过期或已作废?访问的域名、预期用途和本地策略是否匹配?即使这些技术检查全部通过,当前主体又是否拥有这次业务操作的权限?
因此,“证书有效”不是一个判断,而是一组不能互相替代的判断:
能解析,不等于签名正确;签名正确,不等于路径受信;路径受信,不等于状态新鲜;技术验证通过,也不等于业务已经授权。
第三篇已经介绍过 X.509 v3 的字段和扩展。这一篇不再逐项解释字段,而是站到证书使用方一侧,把它们放回真实的验证顺序。
一、先把“证书有效”拆成九个问题
面对一张终端实体证书,验证方至少要完成下面几层判断:
| 层次 | 要回答的问题 | 典型依据 |
|---|---|---|
| 1. 解析 | 输入是不是结构合法、编码可接受的证书 | ASN.1、DER、X.509 结构 |
| 2. 单证书签名 | 颁发者公钥能否验证这张证书的签名 | signatureAlgorithm、签名值、颁发者公钥 |
| 3. 路径构建 | 能否找到一条通往本地信任锚的候选链 | issuer/subject、AKI/SKI、候选中间证书、信任库 |
| 4. 路径验证 | 链上每一级是否有权签发下一级,并满足全部约束 | Basic Constraints、KU、路径长度、名称和策略约束 |
| 5. 时间 | 验证时刻是否落在每张证书的有效期内 | notBefore、notAfter、本地时钟 |
| 6. 状态 | 证书是否被作废,状态信息是否可信且足够新鲜 | CRL、OCSP、状态检查策略 |
| 7. 身份 | 证书声明的服务身份是否与当前连接目标一致 | SAN、RFC 9525 名称匹配规则 |
| 8. 用途与政策 | 这把公钥是否允许用于当前操作 | KU、EKU、Certificate Policies、算法与平台政策 |
| 9. 业务授权 | 这个已验证身份现在能否执行当前动作 | 账号、租户、角色、委托、风控和业务规则 |
这些步骤并不保证所有软件都按同一顺序实现。路径构建可能同时探索多条候选链,状态检查也可能受网络、缓存和平台策略影响。但把判断拆开非常重要:只有这样,错误日志才能说明失败发生在哪一层,系统也不会用“验签成功”掩盖后续检查缺失。
还有一项容易遗漏:收到证书并不能证明对方现在持有对应私钥。私钥控制通常由 TLS 握手签名、客户端认证、挑战应答或业务签名来证明。证书负责绑定身份与公钥,协议负责证明当前参与方能够使用私钥。
二、路径构建:先找出“可能可信”的链
假设服务器发送了下面两张证书:
服务器通常不需要发送根证书。客户端会从自己的信任库中寻找能够接上这条链的信任锚:
这里有三个很容易混淆的事实。
1. 中间证书只是候选材料
服务器发来的中间证书可以帮助客户端构建路径,但它不会因为出现在握手消息中就自动获得信任。验证方仍要检查它的签名、有效期、CA 约束、用途和上级颁发关系。
issuer/subject 名称以及 AKI/SKI 可以帮助筛选颁发者,却不是最终证据。名称相同不代表拥有正确私钥,标识能对应也不代表具备签发权限;真正的路径验证还必须验证签名和约束。
2. 自签名不是信任来源
根证书常常是自签名证书,但“自签名”只描述它用自己的私钥签了自己,不能证明客户端应该相信它。
信任锚之所以可信,是因为它通过操作系统、浏览器、企业策略或应用配置被预先纳入本地信任,而不是因为它能验证自己的签名。
信任锚甚至可以由本地配置的名称、公钥、算法和约束组成,不必完全等同于线上传输的一张根证书。不同平台拥有不同的根证书计划和附加政策,因此,同一张终端证书在不同设备上可能构建出不同路径,甚至得到不同结果。
3. 一张证书可能不止一条路径
交叉签发、多个同名中间 CA、不同根计划和历史兼容链都会产生多条候选路径。路径构建要从可用中间证书、本地缓存、AIA 获取结果和信任库中寻找组合;路径验证则逐条判断候选链是否满足规则。
这两个过程要分开理解:构建成功只代表找到了一条候选链,验证成功才代表这条链在当前输入和策略下可以接受。
三、路径验证:链上的每一级都有约束
找到候选链以后,验证方要从信任锚的约束出发,逐级验证到目标证书。核心不是“把每个签名验一遍”,而是确认每个颁发者既有正确的密钥,也有权签发下一级证书。
CA 身份与签发权限
中间 CA 通常要同时满足:
- Basic Constraints 表明它是 CA;
- Key Usage 在存在时允许
keyCertSign; - 路径长度没有超过
pathLenConstraint; - 名称约束、策略约束和其他上级限制没有被下级绕过;
- 未识别的 critical 扩展会导致拒绝,而不是被静默忽略。
如果一张普通服务器证书恰好能验证另一张证书的签名,它也不会因此变成 CA。密码运算正确与证书授权范围是两件事。
约束会沿路径收紧
验证终端证书时,不能只看它自己的字段。上级 CA 可以通过 Name Constraints 限制下级能签发的命名空间,通过 Certificate Policies 和策略映射限制可接受政策;根计划还可能在信任库元数据中附加约束。
Key Usage 与 Extended Key Usage 也不是“满足一个即可”。前者限制密钥能执行哪类密码操作,后者限制证书能参与哪些应用场景;当两者都存在时,应按交集理解。一个证书拥有数字签名能力,不代表它自然适合 TLS 服务器认证、代码签名或客户端认证。
此外,RFC 5280 给出的是通用 PKIX 路径验证框架。浏览器、操作系统和应用还会叠加算法政策、根计划政策及场景要求,例如拒绝某些签名算法、密钥参数或不符合公开 TLS 规则的证书。
四、时间、名称和用途是三道不同的门
路径受信以后,验证方仍要确认“在这个时间、面对这个对象、为了这个用途”是否可以接受证书。
有效期:证书声明允许在哪段时间使用
验证时刻通常要落在链上相关证书的 notBefore 与 notAfter 之间。时钟错误、时区处理错误和边界值处理差异都会造成验证异常。
有效期检查不能代替吊销检查。证书尚未过期,仍可能因为私钥泄露、主体信息变化或 CA 事件而提前作废;证书已经过期,也不等于历史签名在签署时必然无效,历史证据还需要结合可信时间和当时状态分析。
名称匹配:链可信,但它是不是我要找的服务
TLS 服务身份匹配是路径验证之外的应用层判断。现行 RFC 9525 要求 DNS 服务身份使用 SAN 中的 dNSName,不再回退检查 Common Name。DNS 名称与 IP 地址也必须按对应标识类型检查,不能把字符串看起来相同当成匹配。
通配符的范围同样有限。按照 RFC 9525,它只能作为最左侧标签的完整内容,例如 *.example.com;它不匹配裸域名 example.com,也不应写成 f*.example.com 之类的部分通配。
这解释了一个常见现象:证书链完全可信,浏览器仍然报告名称错误。客户端相信 CA 的签发关系,却发现证书声明的服务不是当前要访问的服务。
用途匹配:这把密钥能不能做当前操作
TLS 服务器验证会检查与服务器认证相符的 EKU 和 KU;客户端认证、代码签名、邮件保护则有各自用途。Certificate Policies 还可表达证书依据的签发政策,但应用是否真正解释某个 policy OID,取决于协议、平台和本地策略。
因此,看到 serverAuth 不等于证书已经通过全部 TLS 验证;看不到某个字段也不能脱离所用协议和验证库直接下结论。用途校验必须放在完整路径和目标场景中进行。
五、吊销检查:查询到状态还不算结束
证书的有效期可能长达数月,但私钥泄露需要在几分钟或几小时内止损。CRL 和 OCSP 都在解决“尚未到期的证书是否已经被 CA 作废”,却有不同的数据和故障模型。
| 机制 | 客户端获得什么 | 主要优势 | 验证时不能漏掉 |
|---|---|---|---|
| CRL | 一段范围内的已吊销证书列表 | 可缓存、可离线批量检查 | 签名、颁发者与范围、更新时间、目标序列号、增量 CRL 的基础列表 |
| OCSP | 针对证书标识的状态响应 | 单次响应较小,可由服务端装订 | 目标证书标识、响应签名、响应者授权、状态和新鲜度 |
CRL 不是下载后搜索序列号那么简单
验证方要确认 CRL 的签名有效、签发者有权发布该 CRL、适用范围覆盖目标证书,并检查 thisUpdate、nextUpdate 等时间。若使用 delta CRL,还要找到匹配的基础 CRL 并正确合并。
一份由错误 CA 签发、已经过期或只覆盖其他分区的 CRL,即使没有目标序列号,也不能推出“证书未作废”。
OCSP 的 good 不是“证书全部有效”
OCSP 响应中的 good 更准确的含义是:对于请求中的证书序列号,响应者没有发现一张当前处于有效期且已作废的证书。它不保证该序列号对应的证书曾经被签发,也不保证响应生成时间落在证书有效期内。客户端仍要检查:
- 响应对应的是否正是当前目标证书;
- 响应签名是否正确,签名者是否被授权响应;
thisUpdate、producedAt、本地最大缓存年龄,以及存在时的nextUpdate,是否满足新鲜度策略;- 响应是否在可接受时间窗口内,是否需要 nonce 或其他防重放措施;
- 路径、名称、用途和本地政策是否另行通过。
RFC 6960 将 nextUpdate 定义为可选字段;缺失时表示更新后的吊销信息随时可用,客户端仍要按本地策略限制响应年龄。RFC 9654 更新了 nonce 扩展;2026 年发布的 RFC 9919 则取代 RFC 5019,为高容量环境给出轻量 Profile。不同部署是否使用 nonce、是否允许缓存以及响应有效期多长,必须按所用 Profile 和应用策略判断,不能从“这是 OCSP”推出统一答案。
状态服务不可用时怎么办
CRL 下载失败、OCSP 超时或服务端没有返回 stapled response 时,验证方必须执行明确的失败策略:
- 硬失败:无法取得满足要求的状态就拒绝;安全边界清晰,但状态服务故障会直接影响可用性;
- 软失败:查询失败时继续,但必须区分“明确为 good”和“根本没有查到”,并承担吊销无法及时生效的风险;
- 场景化策略:高风险操作硬失败,低风险连接配合短期证书、缓存、遥测和补偿控制。
没有一种策略适用于所有系统。真正危险的是实现中默认忽略错误,日志却仍写成“证书有效”。是否强制 OCSP stapling、浏览器是否在线查询以及私有 PKI 如何处理离线环境,也都属于目标平台和业务场景的政策选择。
六、根计划和本地策略决定“谁值得信任”
公开 TLS 证书不仅受 RFC 约束,还受 CA/Browser Forum Baseline Requirements 和浏览器根证书计划管理。CA 进入某个根库,不等于自动进入所有平台;进入根库也不代表它签发的所有用途都会被接受。
截至 2026 年 8 月,Chrome、Mozilla 与 Apple 均维护各自的根证书计划或政策。Chrome Root Program 可以通过根库元数据附加名称约束,并明确其公开 TLS 范围;Mozilla 根库通过 trust bits 区分网站与 S/MIME 等信任用途;企业内部 CA 则通常由设备管理、操作系统策略或应用信任库分发。
由此可以得到一个重要结论:
证书里没有一个
trusted: true字段。信任是证书声明、候选路径、本地信任锚、验证时刻、应用目的和平台政策共同计算出来的结果。
在排查“我的电脑能访问,另一台不能访问”时,除了检查服务器是否漏发中间证书,还要比较两端的信任库、根计划版本、算法政策、系统时间、状态查询能力和名称匹配输入。
七、用 OpenSSL 把验证步骤显式写出来
OpenSSL 可以帮助复现实验,但命令行参数本身也是验证策略的一部分。下面以 OpenSSL 3.6 文档为准。
1. 构建并验证 TLS 服务器证书路径
这里的两个证书输入承担不同角色:
trusted-root.pem通过-CAfile提供本地信任锚;intermediates.pem通过-untrusted提供路径构建候选,它们不会因此变成信任锚。
-purpose sslserver 检查 TLS 服务器用途,-verify_hostname 按 OpenSSL 默认兼容规则执行主机名检查,-show_chain 输出最终采用的链。仅执行 openssl x509 -text 或对终端证书做一次签名验算,都达不到这个验证范围。
这里还要注意一个实现差异:OpenSSL 3.6 的默认主机名检查在没有对应 SAN 时仍可能回退到 subject Common Name,也允许 w*.example.com 这样的部分标签通配符。因此,上面的命令适合排查 OpenSSL 的默认验证结果,却不能证明应用已经严格落实 RFC 9525。
生产 TLS 客户端使用 OpenSSL API 时,需要显式禁用 Common Name 回退和部分通配符,再设置目标主机名:
这两个 flag 分别要求名称检查永不读取 subject Common Name,以及只接受占据完整标签的通配符。应用仍要检查 API 返回值和最终证书验证结果。
2. 使用本地 CRL 检查整条链
-crl_check_all 要求检查链上的相关证书,而不只是终端证书。chain-crls.pem 也必须包含由正确签发者发布、范围和时间均适用的 CRL;把任意 CRL 文件塞进命令不会自动得到可信状态。
3. 观察真实 TLS 握手与 OCSP stapling
-servername 设置 TLS SNI,-verify_hostname 检查证书身份,两者不是同一个选项。-status 请求服务端返回 stapled OCSP response 并在存在时显示;没有响应不必然代表证书无效,是否必须装订由应用政策决定。
还有一个排障陷阱:s_client 是诊断工具,默认即使证书验证发生错误也可能继续握手。加入 -verify_return_error 才会在验证错误时中止,脚本也不应只凭“握手输出很多内容”判断验证成功。
这些命令适合隔离输入和复现问题,但不能自动模拟浏览器的全部根计划、公开 TLS 政策和状态策略。生产应用应使用自身验证库的正式接口,并测试每一种失败路径。
八、技术验证通过之后,业务才开始授权
假设一个双向 TLS 接口已经完成客户端证书验证,并证明请求方控制对应私钥。系统现在可以从证书中取得一个已验证身份,但仍不能仅凭 subject 或序列号放行业务请求。
应用还要把证书身份映射到当前业务主体,并检查:
- 证书是否绑定到正确租户、机构和账号;
- 账号是否已停用,人员是否已经离职或设备是否已退出管理;
- 当前角色是否允许调用这个接口;
- 委托关系是否仍有效,是否超过金额、时间或数据范围;
- 一张更新后的证书是否已经替代旧证书,映射记录是否防止串户;
- 本次请求是否满足重放防护、交易确认和风险策略。
证书中的组织名、OU 或自定义扩展可以成为映射输入,却不应直接充当无限期业务权限。CA 负责按照证书政策声明身份和用途,业务系统才掌握账号状态、组织关系和实时授权。
这条边界也能解释两个常见误区:
| 误区 | 实际上只证明了什么 | 仍缺少什么 |
|---|---|---|
| “证书链通过,所以可以下单” | 公钥与证书身份在当前验证策略下受信 | 当前账号、角色和交易条件授权 |
| “请求里带了证书,所以是本人操作” | 请求携带了公开证书 | 对应私钥控制证明,以及用户对本次操作的意愿 |
PKI 把一个可验证身份交给业务系统;业务系统必须自己决定这个身份此刻可以做什么。
九、一份可落地的验证清单
路径与信任
- 区分终端证书、中间证书候选和本地信任锚;
- 验证链上每一级签名、Basic Constraints、KU、路径长度和 critical 扩展;
- 应用 Name Constraints、Certificate Policies 和信任库附加约束;
- 明确算法、密钥参数和根计划政策,不依赖“库默认应该安全”。
时间、状态与身份
- 检查相关证书有效期,并保证验证主机时钟可信;
- 验证 CRL/OCSP 的签名者、适用目标、范围和新鲜度;
- 把明确
good、明确revoked、unknown和查询失败分开记录; - 为状态服务不可用定义硬失败、软失败或场景化策略;
- TLS 名称按 SAN 和 RFC 9525 匹配,不回退 Common Name;
- 校验 KU、EKU、证书政策与实际应用目的。
协议与业务
- 通过握手签名、挑战应答或业务签名证明私钥控制;
- 将证书身份映射到稳定的业务主体,防止仅按可变显示名称匹配;
- 独立检查账号、租户、角色、委托、交易和风控条件;
- 日志能指出失败层次,不把所有结果压缩成“证书有效/无效”;
- 使用目标平台和真实信任库测试过期、作废、错域名、错用途、缺中间证书和状态服务故障。
十、总结
一张证书被信任,不是因为它长得像证书,也不是因为某次签名运算返回了成功。验证方要先从不受信任输入中构建候选路径,再把路径连接到本地信任锚,逐级执行签名和约束验证,随后检查时间、吊销状态、服务名称、密钥用途与平台政策。
即使这些检查全部通过,结果也只是“在当前策略和时刻,这个证书身份及其公钥可以被接受”。私钥控制仍要由实际协议证明,业务权限仍要由应用根据实时状态决定。
证书没有携带绝对信任。信任是验证方在具体时间、用途、平台政策和业务上下文中计算出来的。
下一篇将继续进入应用层,看看 TLS 如何在握手中使用证书和私钥,数字信封怎样组合对称与非对称密码,以及时间戳如何为签名补上可验证的时间证据。
参考资料
- RFC 5280:Internet X.509 PKI Certificate and CRL Profile,2008-05
- RFC 5280 当前勘误与更新关系(页面核验于 2026-08-06)
- RFC 6960:Online Certificate Status Protocol(OCSP),2013-06
- RFC 9525:Service Identity in TLS,2023-11
- RFC 9654:OCSP Nonce Extension,2024-10
- RFC 9919:面向高容量环境的轻量 OCSP Profile,2026-07
- CA/Browser Forum:TLS Baseline Requirements v2.2.8,2026-06-16
- Chrome Root Program Policy v1.8,2026-02-05
- Mozilla Root Store Policy v3.1,2026-07-01 生效
- Apple Root Certificate Program(页面核验于 2026-08-06)
- OpenSSL 3.6.3:openssl-verify(2026-06-09 发布)
- OpenSSL 3.6.3:openssl-s_client(2026-06-09 发布)
- OpenSSL 3.6.3:X509_check_host 及主机名匹配 flags(2026-06-09 发布)
- OpenSSL 3.6.3:SSL_set1_host 与 SSL_set_hostflags(2026-06-09 发布)
现行资料核验于 2026-08-06。RFC 5280 的更新与勘误、公开 TLS 政策、根计划和软件行为仍会变化;生产系统应按目标平台与发布时间重新核验。
系列文章
- 理解PKI(一):从密码学历史到公钥基础设施
- 理解PKI(二):从 ASN.1 到 DER,数字证书为什么是一串字节
- 理解PKI(三):拆开 X.509 v3——读懂证书的核心字段与扩展
- 理解PKI(四):证书、私钥与容器
- 上一篇:CA 如何签发证书,KMC 如何托管加密密钥
- 本篇:一张证书如何被信任
- 下一篇:证书如何在应用中工作
笔记整理自《PKI、CA 与数字证书技术大全》第 2、9、16、19~20 章,并按现行 RFC、CA/Browser Forum、平台根计划与 OpenSSL 官方文档校正和补充。
