3、加密基础回顾:对称加密(AES)、非对称加密(RSA/ECC)、哈希函数(SHA-256)在固件中的应用

好,咱们进入正题。做 Bootloader 加密,说白了就是跟这三样东西打交道:对称加密、非对称加密、哈希函数。很多新手一上来就搞混,觉得都是加密嘛,有啥区别?我刚开始也这么想,直到在项目里吃了亏才明白——选错加密方式,轻则性能崩盘,重则固件被扒光

今天我就把这三样东西在固件里的实际用法,掰开了讲清楚。你想想看,Bootloader 要干的事无非就是:验证固件是不是官方的、防止别人篡改、防止别人直接读 Flash 把代码抄走。这三件事,正好对应三种不同的密码学工具。

3.1 对称加密:AES 在固件加密中的角色

对称加密,说白了就是加密和解密用同一个密钥。就像你家里的大门,一把钥匙既能锁门也能开门。在固件领域,最常用的就是 AES(Advanced Encryption Standard)。

AES 的特点:速度快、密钥短(128/192/256 位)、适合大批量数据加密。我做过一个项目,固件大小 2MB,用 AES-128-CBC 模式加密,在 STM32F4 上跑,解密时间不到 200 毫秒。这个速度,用户基本无感。

但有个坑——密钥怎么存? 你想想看,Bootloader 里得存着这个密钥才能解密固件吧?那攻击者只要把 Flash 读出来,密钥不就暴露了?

⚠️ 我曾经踩过的坑: 早期一个项目,我把 AES 密钥直接硬编码在 Bootloader 的 .rodata 段里。结果产品上市后,有人用 JTAG 把 Flash 全读出来,密钥明文暴露,固件被随意替换。后来我学乖了,密钥要么用 OTP(一次性可编程)区域存储,要么用芯片自带的唯一 ID 做密钥派生。

在 Bootloader 中的典型用法

  • 固件分发时,先用 AES 加密整个固件镜像
  • Bootloader 启动时,读取加密固件,用预置密钥解密
  • 解密后的固件写入运行区,或者直接 XIP(原地执行)

代码示例(伪代码,实际项目中我会用硬件 AES 加速器):

// 假设密钥已经安全存储在 OTP 区域
uint8_t aes_key[16] = {0x2b, 0x7e, 0x15, 0x16, ...}; 

// 解密固件块
void decrypt_firmware_block(uint8_t *encrypted_data, uint32_t len) {
    AES_ECB_decrypt(encrypted_data, len, aes_key);
    // 注意:实际项目中建议用 CBC 或 GCM 模式,防止重放攻击
}

我个人习惯用 AES-128-GCM 模式。为什么?因为它自带认证标签,能检测数据是否被篡改。CBC 模式只加密不认证,攻击者可以翻转密文比特来搞破坏。嗯,这里要注意,GCM 模式需要额外的 IV(初始化向量),IV 可以明文存储,但每次更新固件必须换新的。

3.2 非对称加密:RSA 与 ECC 的固件签名验证

非对称加密就完全不一样了——加密和解密用不同的密钥。公钥公开,私钥保密。在固件领域,我们通常用它来做数字签名验证,而不是加密整个固件。

为什么?因为非对称加密太慢了。RSA-2048 签名验证一次,在 Cortex-M4 上可能要几百毫秒甚至几秒。你想想看,要是用 RSA 解密整个 2MB 固件,用户得等到天荒地老。

实际做法是这样的

  1. 服务器用私钥对固件的哈希值签名(生成签名数据)
  2. 固件包 = 加密的固件数据 + 签名数据
  3. Bootloader 用公钥验证签名,确认固件来源可信
  4. 验证通过后,再用 AES 密钥解密固件数据

这就是典型的混合加密方案:非对称加密管认证,对称加密管保密。

RSA vs ECC 怎么选? 我个人的经验是:

特性 RSA-2048 ECC(secp256r1)
密钥长度 2048 位 256 位
签名验证速度 较慢(约 300ms @ 200MHz) 较快(约 50ms @ 200MHz)
公钥存储空间 256 字节 32 字节(压缩格式)
安全性 高(同等安全强度下密钥更短)

我建议在资源受限的 MCU 上优先选 ECC。之前有个项目,Flash 只剩 4KB 空间给 Bootloader,RSA 的公钥就占了 256 字节,再加上签名验证代码,根本塞不下。换成 ECC 后,公钥只占 32 字节,整个签名验证模块压缩到了 2KB 以内。

💡 小技巧: 公钥可以存储在芯片的 OTP 区域或者一次性写入的 Flash 扇区。我曾经见过有人把公钥放在固件里一起更新——这等于没锁门!公钥必须在 Bootloader 阶段就固化,永远不能通过固件更新来修改。

3.3 哈希函数:SHA-256 的固件完整性校验

哈希函数,说白了就是数据指纹。不管你的固件是 1KB 还是 1GB,SHA-256 都会给你一个固定 32 字节的摘要。而且这个摘要是单向的——从摘要反推原始数据,理论上不可能。

在 Bootloader 里,SHA-256 主要干两件事:

  • 完整性校验:计算固件的哈希值,跟预置的哈希值对比,看固件有没有被损坏
  • 签名验证的基础:先对固件算哈希,再对哈希值做签名验证(而不是对整个固件做签名)

我遇到过最典型的场景是这样的:OTA 升级时,网络传输可能丢包或者数据错乱。固件下载到一半,突然断电重启。下次启动时,Bootloader 先算一遍 SHA-256,发现跟固件头里记录的哈希值对不上——好,说明固件坏了,直接回滚到旧版本。

// 计算固件区域的 SHA-256 哈希
uint8_t calculated_hash[32];
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, firmware_addr, firmware_size);
sha256_final(&ctx, calculated_hash);

// 跟固件头里存储的哈希对比
if (memcmp(calculated_hash, firmware_header->expected_hash, 32) != 0) {
    // 哈希不匹配,固件损坏
    rollback_to_previous_version();
}

这里有个细节要注意:哈希值本身也需要保护。如果攻击者能同时修改固件和哈希值,那完整性校验就形同虚设。所以哈希值要么存储在安全区域(比如 Bootloader 自己的只读区),要么用非对称签名来保护。

🔑 核心要点: 在 Bootloader 安全体系中,三种加密工具各司其职:
AES:负责固件的机密性(防止别人读你的代码)
RSA/ECC:负责固件的认证性(确保固件来自官方)
SHA-256:负责固件的完整性(确保固件没有被篡改)
三者缺一不可,少一个就是安全漏洞。

最后说一句,别想着只用一种加密搞定所有事。我见过有人只用 AES 加密固件,觉得这样就安全了——结果攻击者直接替换了加密后的固件,设备照样运行,因为 Bootloader 根本没验证固件来源。嗯,这就是典型的只加密不认证的坑。

下一章我会讲怎么把这些工具组合起来,搭建一个完整的 Bootloader 安全启动链。到时候你会看到,这些基础算法是怎么串起来干活的。