2、密码学基础回顾:哈希算法(SHA256)、非对称加密(RSA/ECC)、数字签名原理

做安全启动,绕不开密码学。说白了,安全启动的整个信任链,就是靠这几个基础算法撑起来的。我刚开始接触这块时,总觉得密码学是数学家的事,我们工程师拿来用就行。后来踩了几个坑才明白——不理解原理,你连配置参数都配不对。

这一节,我们快速过一遍三个核心概念:哈希、非对称加密、数字签名。不推公式,只讲工程里怎么用、有什么坑。

2.1 哈希算法:数据的「指纹」

哈希算法,就是把任意长度的数据,压缩成一个固定长度的摘要。这个摘要就像人的指纹——数据变了,指纹一定变。

安全启动里最常用的哈希是 SHA-256。输出 256 位,也就是 32 字节。为什么是 256?因为碰撞概率足够低,低到工程上可以忽略。

SHA-256 的核心特性:

  • 单向性:从摘要反推原始数据,计算上不可行
  • 抗碰撞性:找到两个不同数据产生相同摘要,几乎不可能
  • 雪崩效应:输入改 1 个比特,输出一半以上的比特都会变

我在项目里见过有人用 SHA-1 做固件校验。嗯,SHA-1 已经被破解了,Google 在 2017 年就演示了实际碰撞攻击。所以现在做安全启动,最低标准就是 SHA-256。

看一个简单的计算示例:

// 假设固件镜像的 SHA-256 摘要
原始数据: "Hello, Secure Boot!"
SHA-256:  8e7c5a6c4b3d2f1e0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f

// 改一个字符
原始数据: "Hello, Secure Boot?"
SHA-256:  f1e2d3c4b5a69788796a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4

看到了吗?只改了一个字符,摘要完全不一样。这就是雪崩效应。

工程建议: 硬件如果支持 SHA-256 加速引擎,一定要用。纯软件算 1MB 固件大概要几毫秒,硬件加速可以降到微秒级。这对启动时间敏感的设备很关键。

2.2 非对称加密:公钥与私钥

非对称加密,就是加密和解密用不同的钥匙。一把公开叫公钥,一把私藏叫私钥。公钥加密的数据,只有私钥能解;私钥签名的数据,公钥能验。

安全启动里,我们主要用它的签名验证能力,而不是加密能力。为什么呢?因为固件通常是明文存储的,我们关心的是「这个固件是不是官方发布的」,而不是「固件内容能不能被看到」。

目前主流的两套方案:RSA 和 ECC。

特性 RSA ECC
安全强度 2048 位 ≈ 112 位对称密钥 256 位 ≈ 128 位对称密钥
密钥长度 2048 位(256 字节) 256 位(32 字节)
签名速度 慢(私钥操作) 快
验签速度 快(公钥操作) 较快
硬件支持 非常广泛 逐渐普及

我个人习惯,在资源受限的 MCU 上优先选 ECC。密钥短、签名快,省 Flash 也省 RAM。但如果你用的 SoC 只有 RSA 硬件加速器,那就别纠结,RSA-2048 也够用。

避坑指南: 我曾经在一个项目里看到有人把私钥硬编码在固件里,美其名曰「方便测试」。结果产品差点带着私钥出货。记住:私钥永远只存在于安全的签名服务器或 HSM 里,永远不要出现在嵌入式设备上。

2.3 数字签名:完整性与认证的保证

数字签名,就是把哈希和非对称加密结合起来。流程很简单:

  1. 对固件算 SHA-256,得到摘要
  2. 用私钥对摘要加密,得到签名
  3. 把签名附加到固件后面一起发布
  4. 设备端:算固件摘要,用公钥解密签名,对比两个摘要

为什么不是直接加密整个固件?因为非对称加密慢啊。一个 1MB 的固件用 RSA 加密,可能要算好几秒。而哈希一下,再加密 32 字节的摘要,快得多。

下面这张图展示了完整的签名与验证流程:

数字签名生成与验证流程 数字签名生成与验证流程 签名生成(签名服务器) 固件镜像 SHA-256 哈希 摘要(32 字节) 私钥 数字签名 签名验证(设备端) 固件镜像 SHA-256 哈希 摘要 1 公钥解密签名 → 摘要 2 比较:摘要 1 == 摘要 2 ? 相等 → 固件可信 | 不相等 → 固件被篡改或来源不可信

流程清楚了,我们再看一个实际工程中的签名验证代码片段:

// 伪代码:设备端验证固件签名
int verify_firmware(const uint8_t* firmware, uint32_t fw_len,
                    const uint8_t* signature, uint32_t sig_len,
                    const uint8_t* public_key, uint32_t key_len) {
    
    uint8_t hash[32];  // SHA-256 输出
    
    // 1. 计算固件哈希
    sha256_calculate(firmware, fw_len, hash);
    
    // 2. 用公钥解密签名,得到原始摘要
    uint8_t decrypted_hash[32];
    rsa_decrypt(signature, sig_len, public_key, key_len, decrypted_hash);
    
    // 3. 比较两个摘要
    if (memcmp(hash, decrypted_hash, 32) == 0) {
        return 0;  // 验证通过
    } else {
        return -1; // 验证失败
    }
}

工程细节: 实际项目中,签名通常不是直接对摘要做 RSA 加密,而是使用 PKCS#1 v1.5 或 PSS 填充方案。填充的作用是防止某些数学攻击。我建议新项目直接用 PSS,安全性更高。

2.4 安全启动中的密码学应用

在安全启动里,这些算法是这样配合的:

  • BootROM 里硬编码了厂商的公钥哈希(或者直接存公钥)
  • 第一级 bootloader 的签名由厂商私钥生成
  • BootROM 用公钥验签,通过后才执行第一级 bootloader
  • 第一级 bootloader 再验签下一级,形成信任链

这里有个关键点:公钥本身怎么保证不被篡改?通常的做法是把公钥哈希烧死在芯片的一次性可编程(OTP)区域里。BootROM 先验证公钥的哈希,再用公钥验签。这就是信任根(Root of Trust)的概念。

我曾经踩过的坑: 有次做芯片选型,发现某款芯片的 OTP 区域只有 128 字节。RSA-2048 的公钥是 256 字节,根本放不下。后来只能改用 ECC P-256,公钥只有 64 字节(未压缩格式),刚好塞进去。所以选芯片时,一定要先算好 OTP 空间够不够放你的公钥。

2.5 小结

这一节我们快速回顾了三个基础算法:

  • SHA-256:给固件算指纹,确保完整性
  • RSA / ECC:非对称加密,提供认证能力
  • 数字签名:哈希 + 非对称加密的组合,同时保证完整性和来源认证