3、密码学基础(下):公钥基础设施(PKI)、证书链、X.509证书格式解析

好,咱们接着聊。上一节我们把非对称加密和哈希算法讲透了,这一节要聊的是——这些东西怎么在实际的嵌入式系统里落地。说白了,就是怎么让Bootloader相信“你是谁”。

你想想看,光有RSA或者ECC算法,你只能做签名和验签。但问题来了:公钥从哪来?怎么保证这个公钥没被换过?谁来给公钥做担保?这一连串的问题,就是PKI要解决的。

3.1 公钥基础设施(PKI)到底是什么?

PKI,全称Public Key Infrastructure。名字挺唬人,其实说白了就是一套“信任体系”。

我个人的理解是:PKI = 证书 + 颁发机构 + 验证流程。这三样缺一不可。

在嵌入式安全里,PKI最常见的应用场景就是安全启动。Bootloader手里握着根证书的公钥,然后去验证下一级镜像的签名。这个过程中,证书链起到了“信任传递”的作用。

核心概念:PKI不是算法,而是一套管理公钥的规则。它解决了“公钥分发”这个最头疼的问题。

3.2 证书链——信任是怎么传递的?

证书链,也叫信任链。这个概念我在做车规级MCU安全启动时体会特别深。

举个例子:

  • 根证书(Root CA):自签名,是信任的起点。Bootloader里硬编码的就是它的公钥。
  • 中间证书(Intermediate CA):由根证书签发,用来签发最终实体证书。
  • 实体证书(Leaf Certificate):签发给具体的固件或镜像。

为什么要搞这么复杂?直接根证书签固件不行吗?

嗯,这里要注意:根证书一旦泄露,整个信任体系就崩了。所以根证书通常离线保存,只在安全环境里使用。中间证书可以放在线签发服务器上,即使泄露了,吊销它就行,根证书不受影响。

我的经验:在量产项目中,我建议至少用两级证书链。根证书放在HSM(硬件安全模块)里,中间证书放在CI/CD流水线里。这样既安全又灵活。

3.3 X.509证书格式解析

X.509是证书的标准格式。你在嵌入式里看到的.der、.pem、.crt文件,基本都是这个格式。

一个X.509证书包含以下关键字段:

字段 说明 在Bootloader中的用途
版本号 通常是v3 决定证书结构
序列号 唯一标识 用于吊销列表查询
签名算法 如sha256WithRSAEncryption 告诉验签用哪个算法
颁发者 签发该证书的CA名称 验证证书链时匹配
有效期 起止时间 检查证书是否过期
主体 证书持有者名称 标识镜像发布者
公钥 RSA或ECC公钥 用于验签
扩展项 如Key Usage、Basic Constraints 限制证书用途
签名值 CA对证书的签名 验证证书完整性

我曾经在调试一个安全启动问题时,发现Bootloader验签总失败。查了两天才发现——证书里的Key Usage扩展没有设置digitalSignature标志。Bootloader验签时检查了这个标志,直接拒掉了。嗯,这种坑踩过一次就记住了。

3.4 在Bootloader中如何解析X.509证书?

嵌入式环境资源有限,你不能像PC那样用OpenSSL全套库。我一般用两种方式:

  1. 预解析成结构体:在编译时把证书的关键字段提取出来,硬编码到代码里。Bootloader只做验签,不做完整解析。
  2. 轻量级解析器:用mbedTLS或WolfSSL的证书解析模块,只保留DER解析功能,去掉ASN.1的完整编解码。

下面是一个简化的证书解析示例(伪代码):

// 从固件头部读取证书
uint8_t *cert = get_cert_from_firmware();

// 解析证书基本信息
cert_info_t info;
parse_x509_cert(cert, &info);

// 检查有效期
if (!check_validity(&info)) {
    return ERR_CERT_EXPIRED;
}

// 检查Key Usage
if (!check_key_usage(&info, DIGITAL_SIGNATURE)) {
    return ERR_KEY_USAGE;
}

// 用根证书公钥验证该证书签名
if (!verify_cert_signature(cert, root_pubkey)) {
    return ERR_CERT_SIG_INVALID;
}

// 从证书中提取固件签名公钥
extract_pubkey_from_cert(cert, &fw_pubkey);

// 用固件公钥验证固件签名
if (!verify_firmware_signature(fw_pubkey)) {
    return ERR_FW_SIG_INVALID;
}

return SUCCESS;

重要提醒:千万不要在Bootloader里做完整的ASN.1解析。那会占用大量Flash和RAM。我见过一个项目,因为用了完整解析库,Bootloader体积膨胀了3倍,最后不得不重新设计。

3.5 证书链验证的完整流程

在实际项目中,证书链验证的流程是这样的:

  1. Bootloader从Flash中读取根证书公钥(硬编码在代码里)
  2. 读取固件头部的证书链(通常包含中间证书和实体证书)
  3. 用根证书公钥验证中间证书的签名
  4. 用中间证书的公钥验证实体证书的签名
  5. 从实体证书中提取固件签名公钥
  6. 用该公钥验证固件镜像的签名

每一步都不能省。我曾经在项目中偷懒,跳过了中间证书的验证,结果被攻击者利用了一个中间人攻击的漏洞。从那以后,我再也不敢省略任何一步。

3.6 嵌入式PKI的最佳实践

最后,分享几个我在项目中总结的经验:

  • 证书格式选DER:DER是二进制格式,解析快,体积小。PEM是Base64编码,多了编码解码的开销。
  • 根证书公钥放OTP:一次性可编程存储器,烧进去就改不了。这是最安全的做法。
  • 证书链长度控制:我建议不超过3级。每多一级,Bootloader的验证时间就多一份,而且Flash占用也增加。
  • 考虑证书吊销:虽然Bootloader里做CRL(证书吊销列表)检查比较奢侈,但至少要在固件更新流程中支持。

好了,这一节的内容就到这。PKI和证书链是安全启动的基石,理解透了,后面的章节就好办了。下一节我们聊具体的签名方案实现,到时候会用到今天讲的所有知识。