第二十六讲:嵌入式系统安全——固件加密、安全启动与资源受限环境优化
嵌入式系统安全,说实话,是个“戴着镣铐跳舞”的活。你想想看,一个跑在Cortex-M4上的小设备,可能只有几百KB的Flash,几十KB的RAM,却要完成AES解密、签名校验、安全启动这一整套流程。我在做智能电表项目时,就遇到过这样的困境——芯片算力有限,但安全等级不能降。
这一讲,我们就来聊聊嵌入式环境下的三个核心安全话题:固件加密、安全启动,以及资源受限下的优化策略。
一、固件加密:不只是“加密”那么简单
固件加密,说白了就是防止别人把Flash里的二进制读出来,反汇编,然后抄走你的代码逻辑。但这里有个坑——很多人以为加密就是跑一遍AES,然后把密文存进去就完事了。
我个人习惯的做法是:
- 密钥分层:设备唯一密钥(从芯片UID派生) + 固件加密密钥(由主密钥加密后存储)
- 解密时机:只在启动时解密一次,解密后的明文放在SRAM中运行,Flash中始终保留密文
- 算法选择:资源受限环境下,我推荐AES-128-CTR模式,比CBC少一个IV对齐的麻烦
下面是一个典型的固件加密/解密流程:
// 固件加密(上位机工具执行)
void encrypt_firmware(uint8_t *plaintext, uint32_t len,
uint8_t *key, uint8_t *iv, uint8_t *ciphertext) {
// 使用AES-128-CTR模式
// 注意:IV必须随机生成,每次加密不同
AES_CTR_encrypt(plaintext, len, key, iv, ciphertext);
// 将iv + ciphertext打包写入固件文件
}
// 固件解密(目标设备执行)
void decrypt_firmware(uint8_t *ciphertext, uint32_t len,
uint8_t *key, uint8_t *iv, uint8_t *plaintext) {
// 从固件头部提取IV
memcpy(iv, ciphertext, 16);
// 解密主体
AES_CTR_decrypt(ciphertext + 16, len - 16, key, iv, plaintext);
// 明文直接映射到SRAM执行区
}
二、安全启动:信任链的起点
安全启动,说白了就是确保你设备上跑的每一行代码,都是你签过名的、没被篡改过的。这个信任链从BootROM开始,一级一级往上验证。
为什么会需要这个?我遇到过一件事:有个客户的产品被破解了,黑客通过修改固件中的某个标志位,绕过了付费功能。嗯,从那以后,我再也不敢在量产产品里省略安全启动。
一个典型的安全启动流程是这样的:
- BootROM:芯片出厂固化的只读代码,验证下一级Bootloader的签名
- Bootloader:验证固件镜像的签名和完整性
- 固件镜像:包含签名头和加密后的代码段
签名验证的核心代码,我一般这样写:
// 使用ECDSA验证固件签名(资源受限优化版)
int verify_firmware_signature(uint8_t *firmware, uint32_t fw_len,
uint8_t *signature, uint8_t *pubkey) {
uint8_t hash[32];
// 1. 计算固件哈希(使用SHA-256,硬件加速可用时优先)
SHA256_compute(firmware, fw_len, hash);
// 2. ECDSA验签(使用secp256r1曲线)
// 注意:这里使用预计算好的公钥点,避免每次重新计算
if (ECDSA_verify(hash, signature, pubkey) != 0) {
return -1; // 签名验证失败,拒绝启动
}
return 0; // 验证通过
}
三、资源受限环境优化:每一字节都要精打细算
嵌入式环境里,你没法像PC那样随便malloc几MB内存。我记得有一次做低功耗门锁项目,整个芯片只有16KB RAM,AES解密一次就要吃掉4KB的缓冲区。怎么办?只能优化。
我总结了几条实战经验:
| 优化方向 | 具体做法 | 效果 |
|---|---|---|
| 算法选择 | 使用AES-128替代AES-256,安全性足够且速度快30% | 减少Flash占用约8KB |
| 密钥派生 | 用硬件唯一ID + 固定盐值,避免存储密钥 | 节省4KB非易失存储 |
| 流式解密 | 边解密边执行,不一次性加载整个固件 | RAM占用从16KB降到2KB |
| 签名算法 | 使用Ed25519替代RSA-2048,签名小且验证快 | 签名大小从256字节降到64字节 |
这里有一个流式解密的示例,我个人觉得非常实用:
// 流式解密执行(资源受限环境专用)
void stream_decrypt_execute(uint8_t *ciphertext_start, uint32_t total_len,
uint8_t *key, uint8_t *iv) {
uint8_t buffer[256]; // 小缓冲区,仅256字节
uint32_t offset = 0;
uint32_t chunk_size;
while (offset < total_len) {
chunk_size = min(256, total_len - offset);
// 解密一个块
AES_CTR_decrypt_chunk(ciphertext_start + offset,
chunk_size, key, iv, buffer);
// 直接执行解密后的代码(注意:需要RAM可执行权限)
execute_code(buffer, chunk_size);
offset += chunk_size;
}
}
四、知识体系总览
下面这张图,是我对嵌入式安全三个核心维度的总结。你可以把它当作一个检查清单:
你看,这三个维度其实是相互关联的。固件加密做得好,安全启动的负担就轻;资源优化到位,才能给加密算法留出足够的运行空间。我在实际项目中,通常会把这三者放在一起做系统级设计,而不是各自为政。
好了,这一讲的内容就到这里。嵌入式安全是个实践性很强的领域,光看理论是不够的。我建议你找一块开发板,把今天讲的AES-CTR解密和安全启动流程亲手跑一遍。遇到问题?那正是学习的机会。
公众号:蓝海资料掘金营,微信deep3321