3、密码学基础(下):公钥基础设施(PKI)、证书链与根证书
上一讲我们聊了对称与非对称加密、哈希和签名。这些算法单独拿出来,其实都挺完美的。但一放到真实世界里,问题就来了——你怎么确定你拿到的公钥,真的是对方的?
我在一个物联网项目里就踩过这个坑。设备A要给设备B发一条加密指令,B把公钥通过Wi-Fi传过来。看起来没问题对吧?但中间人完全可以把这个公钥换成自己的。A用假公钥加密,中间人解密后篡改数据,再用真公钥转发给B。整个过程,A和B都浑然不觉。
所以,光有密码学算法是不够的。我们需要一套机制,来回答那个灵魂拷问:“你怎么证明你是你?”
这就是公钥基础设施(PKI)要解决的问题。
3.1 什么是PKI?
PKI不是某个算法,也不是某个软件。它是一整套策略、流程、硬件、软件的组合。说白了,就是一套“发身份证、查身份证、吊销身份证”的管理体系。
一个典型的PKI系统包含以下几个核心角色:
| 角色 | 英文 | 职责 |
|---|---|---|
| 证书颁发机构 | CA | 签发、吊销证书,是整个PKI的信任锚点 |
| 注册机构 | RA | 负责验证申请者的身份,把审核结果告诉CA |
| 证书库 | Repository | 存储已签发的证书和吊销列表(CRL) |
| 终端实体 | End Entity | 持有证书的个人、设备或服务器 |
你想想看,如果没有RA,CA就得亲自去验证每一个申请者的身份。对于大型系统来说,这根本不现实。我经手的一个车联网项目,每天有上万台新设备上线。CA根本忙不过来,必须靠RA做第一道审核。
3.2 数字证书长什么样?
证书就是PKI里的“身份证”。最通用的标准是X.509 v3。一个证书里包含的信息,比你想象的多得多。
我习惯把证书结构拆成三块来看:
- 主体信息:谁持有这个证书(设备ID、域名、组织名)
- 公钥信息:持有者的公钥,以及使用的算法
- 签发信息:谁签发的、有效期、签名值
用OpenSSL看一下证书内容,最直观:
openssl x509 -in device.crt -text -noout
输出里你会看到类似这样的字段:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0x12345678
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=CN, O=MyCA, CN=Root CA
Validity
Not Before: Jan 1 00:00:00 2024 GMT
Not After : Dec 31 23:59:59 2025 GMT
Subject: C=CN, O=MyDevice, CN=device-001
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
X509v3 extensions:
X509v3 Basic Constraints: CA:FALSE
X509v3 Key Usage: Digital Signature, Key Encipherment
Signature Algorithm: sha256WithRSAEncryption
a1:b2:c3:... (签名值)
嗯,这里要注意Basic Constraints这个扩展。它标记了这个证书能不能继续签发下级证书。CA证书是CA:TRUE,终端设备证书是CA:FALSE。如果搞反了,安全体系就形同虚设。
3.3 证书链与信任传递
单靠一个CA签所有证书,风险太集中。CA的私钥一旦泄露,整个系统就完了。所以实际部署中,我们几乎都用多级证书链。
典型的证书链分三层:
- 根CA(Root CA):离线保存,极少使用。自签名证书。
- 中间CA(Intermediate CA):在线工作,由根CA签发。可以有多级。
- 终端证书(Leaf Certificate):由中间CA签发,给设备或服务器用。
为什么要这么设计?我举个实际例子。在安全启动里,根CA的私钥通常放在硬件安全模块(HSM)里,平时不通电。只有每年更新中间CA时,才把HSM接上。这样即使生产线的服务器被黑,攻击者也拿不到根CA的私钥。
验证证书链时,是从叶子往根追溯:
- 用中间CA的公钥验证叶子证书的签名
- 用根CA的公钥验证中间CA的签名
- 根CA是自签名的,它的信任来自“预置在信任存储区”
这个过程,说白了就是“我信你,因为你是我信的人担保的”。
下面这张图展示了三级证书链的结构和验证流程:
3.4 根证书的信任分发
根证书是整个信任链的起点。但问题是,根证书本身没有上级给它签名。那你怎么信任它?
答案是:预置。
在安全启动场景里,根证书的公钥(或者它的哈希)是直接烧录在芯片的一次性可编程(OTP)存储器里的。芯片出厂时就写死了,之后任何人都改不了。
我参与的一个TEE项目,根证书哈希就放在eFuse里。产线烧录时,会同时写入这个哈希。每次启动时,固件先读取eFuse里的值,然后校验根证书是否匹配。不匹配?直接拒绝启动。
3.5 证书吊销:信任的撤回
证书不是永久的。私钥泄露、设备报废、员工离职——这些情况都需要把证书“作废”。
常用的吊销机制有两种:
| 机制 | 原理 | 适用场景 |
|---|---|---|
| CRL(证书吊销列表) | CA定期发布一个黑名单,列出所有被吊销的证书序列号 | 离线设备、网络受限环境 |
| OCSP(在线证书状态协议) | 实时查询某个证书是否有效 | 在线服务、低延迟要求 |
我个人习惯在嵌入式设备上用CRL。原因很简单——OCSP需要联网,而很多IoT设备根本没有稳定的网络连接。但CRL有个问题:时效性。如果CA刚吊销了一个证书,但设备上次拉取CRL是一周前,那这一周内它就是“带病运行”。
3.6 安全启动中的PKI实践
好了,理论说完了。咱们看看PKI在安全启动里到底怎么落地。
一个典型的安全启动PKI架构是这样的:
- 根CA:离线保存在HSM里,只用来签发中间CA。一年用一次。
- 代码签名CA:一个专门的中间CA,只用来签名固件镜像。
- 设备身份CA:另一个中间CA,用来签发每个设备的身份证书。
为什么要分开?我在一个项目里吃过亏。当时代码签名和设备身份共用一个CA。结果固件签名流程需要联网,而设备身份注册也需要联网。两个流程混在一起,权限边界模糊。后来一次误操作,签名CA的私钥被暴露了。虽然及时更换,但所有已出货的设备都得召回升级——成本惨痛。
从那以后,我坚持职责分离。代码签名CA的私钥只放在隔离的签名服务器上,不联网。设备身份CA可以放在线,但权限严格受限。
最后,给一张完整的PKI在安全启动中的流程图:
这张图里,根CA只做一件事:签发两个中间CA。代码签名CA只签固件,设备身份CA只签设备。两条链路互不干扰。即使设备身份CA被攻破,攻击者也拿不到代码签名权限,无法伪造固件更新。
这就是PKI的精髓——通过分层和隔离,把信任风险控制在可接受的范围内。