4、密码学基础(下):数字签名原理,公钥基础设施(PKI)与证书链
好,咱们接着聊密码学。上一节我们把对称加密、非对称加密、哈希函数这些基础工具都过了一遍。这一节,我们要用这些工具搭一个真正的“安全体系”——数字签名、PKI 和证书链。
说白了,加密解决的是“别人看不懂”的问题,而签名解决的是“这东西到底是不是你发的”问题。在嵌入式安全里,后者往往更重要。你想想看,如果固件被篡改了,你还在傻乎乎地升级,那后果……嗯,我见过太多这样的案例了。
4.1 数字签名:不只是“签名”那么简单
数字签名,本质上是用私钥对数据的哈希值进行加密。验证方用公钥解密,比对哈希值。如果一致,说明数据没被改过,而且确实是你签的。
我个人习惯把数字签名比作“盖章+封条”。盖章证明身份,封条保证完整性。缺一不可。
- 发送方计算数据的哈希值(比如 SHA-256)
- 用私钥加密这个哈希值,得到签名
- 发送数据 + 签名给接收方
- 接收方用公钥解密签名,得到哈希值 A
- 接收方自己计算数据的哈希值 B
- 比较 A 和 B,一致则验证通过
我在项目中遇到过一个问题:有人直接把整个固件用私钥加密,当作签名。这其实是个误区。非对称加密很慢,尤其是大文件。正确的做法是只签哈希值,而不是签整个文件。这个坑,我踩过一次,后来再也没犯过。
4.2 公钥基础设施(PKI):信任的起点
好,现在你有了签名和验签的能力。但问题来了:你怎么拿到对方的公钥?
如果公钥被中间人替换了,那整个签名体系就崩塌了。这就是 PKI 要解决的问题——建立一个可信的第三方,来管理公钥的发放和验证。
PKI 的核心组件包括:
- CA(证书颁发机构):信任的锚点,负责签发证书
- 证书:把公钥和身份信息绑定在一起,由 CA 签名
- CRL(证书吊销列表):记录哪些证书已经失效
- RA(注册机构):辅助 CA 做身份审核
4.3 证书链:层层信任,环环相扣
证书链,说白了就是“你信任我,我信任他”的传递关系。根 CA 自签自己的证书,然后签发中间 CA,中间 CA 再签发终端实体证书。
为什么要搞这么复杂?直接让根 CA 签发所有终端证书不行吗?
嗯,这里要注意:根 CA 的私钥是最高机密,如果泄露了,整个体系就完了。所以根 CA 通常离线保存,只在签发中间 CA 时才上线。日常的证书签发由中间 CA 完成。这样即使中间 CA 的私钥泄露,也只是影响一部分,根 CA 可以吊销它,重新签发。
我曾经帮一家车厂设计过 OTA 升级的证书链。他们的做法是:
- 根 CA 离线,存储在硬件安全模块(HSM)里
- 中间 CA 用于签发每个车型的固件签名证书
- 终端证书用于实际签名固件,有效期只有 1 年
这样即使某个车型的证书泄露,也不会影响其他车型。而且每年轮换证书,安全性大大提高。
4.4 嵌入式场景下的 PKI 实践
在嵌入式设备里,PKI 的实现和 PC 端有很大不同。资源受限、网络不稳定、设备数量巨大,这些都是挑战。
我建议的做法是:
- 预置根证书:在芯片出厂时,把根 CA 证书烧入 OTP 或 eFuse
- 证书链验证:设备启动时,验证固件签名证书的合法性,一直追溯到根证书
- 证书吊销:通过黑名单机制,或者设置短有效期,来应对证书泄露
- 硬件加速:尽量使用芯片自带的硬件加密引擎,别用软件实现,太慢了
下面这张图展示了嵌入式安全启动中典型的证书链结构:
4.5 一个简单的签名验证代码示例
下面是一个用 OpenSSL 做签名验证的简化示例。实际嵌入式开发中,你会用 mbedTLS 或 WolfSSL 这类轻量级库。
// 伪代码:固件签名验证
int verify_firmware(const uint8_t* firmware, size_t fw_len,
const uint8_t* signature, size_t sig_len,
const uint8_t* cert, size_t cert_len) {
// 1. 解析证书,提取公钥
x509_cert_t* x509 = x509_parse(cert, cert_len);
if (!x509) return -1;
// 2. 验证证书链(从当前证书追溯到根证书)
if (!verify_cert_chain(x509, root_ca_cert)) {
return -2; // 证书不可信
}
// 3. 计算固件哈希
uint8_t hash[32];
sha256(firmware, fw_len, hash);
// 4. 用证书中的公钥验证签名
if (!rsa_verify(x509->public_key, hash, 32, signature, sig_len)) {
return -3; // 签名不匹配
}
return 0; // 验证通过
}
4.6 常见问题与避坑
最后,分享几个我在项目中踩过的坑:
- 证书过期:设备可能几年不联网,证书过期了都不知道。我建议用长有效期根证书 + 短有效期终端证书的组合。
- 时间同步:证书验证需要准确时间。没有 RTC 的设备怎么办?可以用固件版本号或生产日期作为参考。
- 性能问题:RSA-2048 签名验证在低端 MCU 上可能要几百毫秒。如果启动时间敏感,可以考虑用 ECC 算法,速度快很多。
- 存储空间:证书链可能占用几十 KB。对于 Flash 只有 256KB 的小设备,这是个不小的开销。
我曾经在一个项目里,因为没处理好证书过期问题,导致一批设备在某个日期后全部无法升级。后来我们加了一个“宽限期”机制:证书过期后 30 天内仍然允许升级,但会记录日志。这样既保证了安全,又给了用户缓冲时间。
好了,这一节的内容就到这里。数字签名和 PKI 是嵌入式安全的基石,理解透了,后面的安全启动和固件升级就顺理成章了。
- 数字签名 = 私钥加密哈希值,公钥解密验证
- PKI 解决“公钥从哪来”的信任问题
- 证书链实现分层信任,根 CA 必须离线保护
- 嵌入式场景要关注性能、存储、时间同步等实际问题
可可精品资料库