3、密码学基础(上):对称加密(AES)、非对称加密(RSA/ECC)、哈希函数(SHA-256)在升级中的应用

做固件升级安全,说白了就是跟三件事打交道:加密、签名、校验。这三件事背后站着三大类密码学工具——对称加密、非对称加密、哈希函数。很多工程师觉得密码学是数学家的活儿,自己会用库就行。但我做了这么多年嵌入式安全,见过太多「会用库但用错」的翻车现场。

这一章,咱们就聊聊这三种工具在固件升级里的真实用法。不扯复杂的数学公式,只讲你写代码时真正需要知道的东西。

3.1 对称加密:AES 在固件加密传输中的应用

对称加密,就是加密和解密用同一把钥匙。你想想看,这像什么?像你家大门钥匙——你自己能开门,给室友配一把也能开。优点是快,特别快;缺点嘛,钥匙怎么安全地交给对方,是个大问题。

在固件升级场景里,AES 主要用来加密固件镜像本身。我习惯用 AES-256-GCM 模式,原因很简单:它不光加密,还能做完整性校验(GCM 模式自带认证标签)。

核心要点:固件升级中,AES 只负责「加密传输」,不负责「身份验证」。也就是说,它能防止别人偷看你的固件,但没法证明这个固件是谁发的。

AES 的几种工作模式,我该怎么选?

模式 特点 固件升级推荐度
ECB 简单,但相同明文块产生相同密文块,有模式泄露风险 ❌ 不推荐
CBC 需要 IV,加密串行,解密可并行 ⚠️ 可用,但需配合 HMAC
CTR 可并行,无填充,但无认证 ⚠️ 可用,但需配合 HMAC
GCM CTR + GMAC 认证,一次完成加密和完整性校验 ✅ 强烈推荐

我个人项目中几乎只用 GCM 模式。有一次,我在一个 IoT 设备上用了 CBC 模式,结果忘了加 HMAC,固件在传输过程中被篡改了一个字节,设备刷完直接变砖。嗯,从那以后,我再也不敢省掉认证这一步了。

实战建议:在资源受限的 MCU 上,AES-128 通常够用,而且比 AES-256 快 40% 左右。如果你的设备有硬件 AES 加速器,那用 AES-256 也没问题。

3.2 非对称加密:RSA 与 ECC 在签名验证中的对决

非对称加密,就是加密和解密用不同的钥匙——公钥和私钥。公钥可以公开,私钥自己藏好。这解决了对称加密「钥匙怎么安全传递」的难题。

在固件升级里,非对称加密主要干一件事:数字签名。服务器用私钥给固件签名,设备用公钥验证签名。只要私钥不泄露,谁也别想伪造固件。

RSA vs ECC:谁更适合嵌入式设备?

这个问题我经常被问到。直接说结论:ECC 在嵌入式里更有优势。

对比项 RSA-2048 ECC P-256
密钥长度 2048 位 256 位
签名长度 256 字节 64 字节
签名速度 较慢 快
验证速度 快 较快
安全等级 112 位 128 位
存储占用 大 小

你看,ECC P-256 在安全等级更高的情况下,密钥和签名都短得多。这对嵌入式设备来说太重要了——Flash 和 RAM 都是钱啊。

不过,RSA 也有它的优势:兼容性好。很多老旧的 Bootloader 只支持 RSA 验签。我遇到过一家客户,他们的设备用了 10 年的 RSA-1024,想升级到 ECC,结果 Bootloader 不支持,只能整个换芯片。所以,选什么算法,还得看你现有的硬件和软件生态。

避坑指南:我曾经在一个项目里用了 ECC 签名,但没注意设备上的随机数生成器质量太差,导致每次签名验证都失败。后来发现是 ECC 签名算法需要高质量的随机数来做「nonce」。如果你的设备没有硬件 TRNG,建议用确定性 ECDSA(RFC 6979),避免随机数问题。

3.3 哈希函数:SHA-256 如何保证固件完整性

哈希函数,就是把任意长度的数据,压缩成一个固定长度的「指纹」。这个指纹有个特点:原文改一个比特,指纹就完全变了。而且,从指纹反推原文,理论上不可能。

在固件升级里,哈希函数主要用在两个地方:

  1. 固件完整性校验:下载完固件后,算一下哈希值,跟官方公布的对比,就知道固件有没有被篡改。
  2. 数字签名的一部分:先对固件算哈希,再对哈希值签名。这样签名速度快,而且签名长度固定。

SHA-256 是目前最主流的选择。它输出 256 位(32 字节)的哈希值,碰撞概率极低,安全性足够。

一个常见的误解:很多人以为哈希函数能「加密」数据。不对,哈希是单向的,不可逆的。它只能用来验证数据有没有被改过,不能用来隐藏数据内容。

哈希在固件升级中的典型流程

// 伪代码:固件完整性校验流程
uint8_t firmware_hash[32];
sha256_calculate(firmware_data, firmware_size, firmware_hash);

// 从安全存储中读取官方哈希值
uint8_t expected_hash[32];
read_from_secure_storage(expected_hash);

// 比较
if (memcmp(firmware_hash, expected_hash, 32) == 0) {
    // 固件完整,可以刷写
    start_firmware_update();
} else {
    // 固件被篡改,拒绝升级
    report_error("Firmware integrity check failed");
}

这段代码看起来简单,但实际项目中坑不少。比如,memcmp 是时间不安全的——攻击者可以根据比较时间推断出哈希值。我建议用 timing_safe_memcmp 或者自己实现一个恒定时间的比较函数。

3.4 三种密码学工具如何协同工作?

单独看每种工具,功能都很明确。但在实际固件升级系统里,它们是配合使用的。下面这张图展示了典型的协同流程:

固件升级密码学协同工作流程 服务器端(签名与加密) ① 原始固件镜像 ② SHA-256 计算哈希值 ③ ECC 私钥签名哈希值 ④ AES-256-GCM 加密固件 ⑤ 打包(密文 + 签名 + IV) 网络传输 设备端(解密与验证) ⑥ 接收密文 + 签名 + IV ⑦ AES-256-GCM 解密固件 ⑧ SHA-256 计算哈希值 ⑨ ECC 公钥验证签名 ⑩ 验证通过,刷写固件 说明:AES 保证机密性,SHA-256 保证完整性,ECC 保证真实性。三者缺一不可。

这个流程看起来步骤多,但每个环节都有它的意义。你想想看,如果只用 AES 加密,攻击者虽然看不到固件内容,但可以篡改密文,设备解密后刷入损坏的固件,照样变砖。如果只用签名,固件内容在传输过程中是明文,任何人都能拿到你的知识产权。

所以,加密 + 签名 + 哈希,三者配合,才能构建一个完整的固件升级安全体系。

我的个人习惯:在资源允许的情况下,我会把公钥直接编译进 Bootloader 的只读区域,并在芯片出厂前锁定。这样即使攻击者拿到了 Flash 镜像,也无法替换公钥。另外,AES 的密钥我通常用 ECDH 密钥交换来协商,而不是写死在固件里——写死密钥的做法,我见过太多翻车的案例了。

3.5 小结:选型决策树

最后,给你一个简单的选型决策思路:

  • 你的 MCU 有硬件加密引擎吗? 有 → 优先用 AES-256-GCM + ECC P-256。没有 → 考虑 AES-128-GCM + RSA-2048(RSA 验签可以纯软件实现,速度还行)。
  • 你的固件大小超过 1MB 吗? 是 → 建议分块计算哈希,避免一次性加载到 RAM。否 → 一次性计算即可。
  • 你的设备需要支持远程升级吗? 是 → 必须用非对称签名,因为对称密钥无法安全分发。否 → 可以用预共享密钥的 HMAC 做完整性校验,更简单。

密码学基础这部分,咱们先聊到这儿。记住一句话:密码学工具本身没有错,错的是用错场景的人。选对工具,用对模式,你的固件升级就能安全一大半。