4、数字签名与证书:非对称加密基础、签名生成与验证流程、证书链管理

好,我们进入第四章。这一章讲的是数字签名和证书,说白了就是解决「你怎么证明这东西是你发的」和「我怎么相信你手里的公钥是真的」这两个问题。

我在项目里见过太多人把签名和加密搞混。加密是为了不让别人看,签名是为了证明是你写的。两个概念完全不同,但用的都是非对称加密那一套东西。我们先从基础开始。

4.1 非对称加密基础

非对称加密,就是一对钥匙:公钥和私钥。公钥可以公开,私钥自己藏好。

我习惯用一个比喻:公钥是锁,私钥是钥匙。你把锁发给全世界,大家用锁把箱子锁好寄给你,只有你手里的钥匙能打开。反过来,签名就是你把钥匙当印章盖下去,大家用你给的锁来验证这个印章是不是真的。

常见的非对称算法有 RSA 和 ECC。RSA 基于大数分解,ECC 基于椭圆曲线离散对数。ECC 在同等安全强度下密钥更短,计算更快,在嵌入式设备里我优先选 ECC。

算法 安全强度 密钥长度 签名速度 验证速度
RSA-2048 112 位 2048 bit 慢 快
RSA-3072 128 位 3072 bit 更慢 较快
ECC P-256 128 位 256 bit 快 快
ECC P-384 192 位 384 bit 较快 较快
我的建议:新项目直接上 ECC P-256 或 P-384。RSA 虽然生态成熟,但密钥太长,在资源受限的 MCU 上签名慢得让人抓狂。我曾经在一个 Cortex-M4 上用 RSA-2048 做签名,等了快两秒——这在设备注册流程里根本没法接受。

4.2 签名生成与验证流程

签名不是直接对原始数据加密,那样太慢。实际做法是:先对数据做哈希,得到摘要,再用私钥加密这个摘要。验证时,用公钥解密摘要,再对比自己算出来的哈希值。

流程其实很简单:

  1. 签名方:对固件/数据做哈希(比如 SHA-256)
  2. 签名方:用私钥加密哈希值,得到签名
  3. 签名方:把原始数据 + 签名一起发出去
  4. 验证方:对收到的数据做同样的哈希
  5. 验证方:用公钥解密签名,得到原始哈希
  6. 验证方:对比两个哈希,一致则通过

嗯,这里要注意:哈希算法必须和签名算法匹配。比如 ECDSA 签名内部已经包含了哈希步骤,你不需要额外再算一次。我见过有人用 SHA-256 算完哈希,又用 ECDSA 签了一遍——结果 ECDSA 内部又算了一次哈希,等于做了两次,浪费算力。

核心要点:签名验证的本质是「哈希对比」。私钥加密的是哈希,不是原始数据。公钥解密后对比哈希,而不是对比原始数据。

下面这张图把整个流程画清楚了:

数字签名生成与验证流程 签名方(私钥签名) 原始数据 SHA-256 哈希 私钥加密 生成签名 传输:原始数据 + 签名 验证方(公钥验证) 提取原始数据 SHA-256 哈希 公钥解密签名 得到原始哈希 对比两个哈希值 一致 → 验证通过
避坑提醒:我曾经在一个项目里发现,验证方没有检查签名长度就直接 memcpy,结果攻击者传了个超长签名,直接栈溢出。签名验证的每一步都要做边界检查,尤其是嵌入式环境,内存紧张,更容易出这类问题。

4.3 证书链管理

好,现在你会签名验证了。但有个问题:你手里的公钥是哪里来的?如果攻击者伪造了一个公钥发给你,你用它去验证签名,那一切就白搭了。

这就是证书要解决的问题。证书说白了就是「一个可信第三方帮你担保这个公钥是真的」。这个第三方叫 CA(Certificate Authority)。

证书链的结构是这样的:

  • 根证书:自签名,最顶层的信任锚点。一般预置在设备里,烧死在 ROM 中,不可更改。
  • 中间证书:由根证书签发,用于签发设备证书。根证书不直接签设备,减少根证书泄露风险。
  • 设备证书:由中间证书签发,包含设备的公钥和身份信息。

验证流程是从下往上:设备证书 → 中间证书 → 根证书。每一级都用上一级的公钥验证下一级的签名,直到根证书。根证书是自签名的,你信任它是因为它被预置在你的设备里。

我习惯把证书链叫做「信任链」。你想想看,如果根证书被篡改了,整条链就全废了。所以根证书的存储是安全启动里最敏感的一环。

证书层级 签发者 存储位置 安全要求
根证书 自签名 设备 ROM / OTP 不可写,只读
中间证书 根证书 固件镜像 / 安全存储 需验证签名
设备证书 中间证书 固件镜像 / 运行时加载 需验证签名

下面这张图展示了证书链的层级关系:

证书链信任层级 根证书(自签名) 预置在设备 ROM,不可更改 中间证书 由根证书签发 设备证书 由中间证书签发,包含设备公钥 验证方向:从下往上,每一级用上一级公钥验证签名
实际经验:我在做设备注册流程时,证书链一般只保留两级:根证书 + 设备证书。中间证书在服务器端管理,设备端不存。这样设备只需要验证一级签名,启动速度更快。根证书烧死在 OTP 里,设备证书放在固件里一起签名。每次注册时,设备把证书链发回服务器,服务器验证完整链路。

4.4 证书格式与解析

证书最常用的格式是 X.509。里面包含:版本号、序列号、签名算法、签发者、有效期、主体、公钥、扩展信息、签名值。

在嵌入式设备里解析 X.509 证书是个麻烦事。证书是 DER 编码的 ASN.1 结构,解析起来很费劲。我建议不要自己写解析器,直接用现成的库,比如 mbedTLS 或 WolfSSL。

但有一点要注意:解析证书时一定要检查有效期。我见过一个案例,设备证书过期了,但验证逻辑没检查这个字段,结果设备一直用过期证书注册,被服务器拒绝。排查了半天才发现是证书过期了。

关键检查项:验证证书时至少检查以下内容:
1. 签名是否有效(用上一级公钥验证)
2. 证书是否在有效期内(notBefore / notAfter)
3. 证书用途是否匹配(Key Usage 扩展)
4. 证书是否被吊销(CRL / OCSP,可选但推荐)

嗯,说到吊销,嵌入式设备一般不做在线 OCSP 检查,因为网络开销太大。更常见的做法是预置 CRL 列表,或者干脆用短有效期证书,过期就换。我个人倾向后者,省事。

好了,这一章就到这里。数字签名和证书链是安全启动的基石,理解透了,后面的流程设计就顺了。