嵌入式设备软件数字篡改是保障设备安全的核心需求。本文基于工程实践,梳理嵌入式设备软件数字签名系统的设计要点、实操步骤与避坑指南,帮助开发团队快速落地可复用的安全方案。
一、前期准备:明确核心需求与技术选型
在启动设计前,需先明确3个关键问题,避免后期返工:
1.1 确定核心目标与约束
- 核心目标:仅防篡改(验证软件完整性)?还是需兼顾身份认证(确认软件来源)?本文聚焦"防篡改+安全启动"核心场景。
- 硬件约束:设备CPU架构(ARM Cortex-M/RISC-V/AVR)、内存大小(<64KB需优先轻量化方案)、是否集成硬件加密模块(如ESP32的Secure Element、STM32的CryptoCell)。
- 合规要求:国内场景需支持国密SM2/SM3,出口场景需符合FIPS 140-3或CC EAL4+。
1.2 关键算法选型(避坑指南)
| 需求场景 | 推荐算法组合 | 不推荐方案 | 原因分析 |
|---|---|---|---|
| 资源受限设备(<128KB RAM) | ECC-256 + SHA-256 | RSA-2048 + SHA-1 | RSA运算耗时是ECC的5-10倍,SHA-1已存在碰撞风险 |
| 国内合规场景 | SM2 + SM3 | ECC-384 + SHA-512 | 国密算法需通过商用密码认证,且硬件支持更成熟 |
| 高性能设备(Cortex-M7/MPU) | ECC-384 + SHA-256(硬件加速) | 纯软件实现ECC/SM2 | 硬件加速可将签名时间从200ms降至30ms内 |
实操建议:优先选择芯片自带硬件加密模块的方案(如STM32L5、ESP32-S3),若无硬件加速,需使用microECC(ECC)、GMSSL(国密)等轻量级库,避免引入OpenSSL等大体积依赖。
二、系统架构设计:3层核心模块落地步骤
嵌入式数字签名系统需区分"签名生成端"(PC/云端)和"签名验证端"(设备端),重点落地设备端的验证逻辑,架构分为3层:
2.1 签名生成层(PC/云端:3步实现)
负责为软件镜像生成合法签名,需在安全环境(如离线PC、HSM硬件)操作:
-
步骤1:准备软件镜像
将编译后的固件(如Bootloader、内核、APP)合并为完整镜像(.bin/.hex),若分分区存储(如Bootloader+APP),需分别签名。 -
步骤2:计算镜像哈希
使用SHA-256/SM3计算镜像哈希值,避免直接对大文件签名(减少计算量),命令示例(Linux):bash# SHA-256计算 openssl dgst -sha256 firmware.bin -binary -out firmware.sha256 # SM3计算(需安装GMSSL) gmssl dgst -sm3 firmware.bin -binary -out firmware.sm3 -
步骤3:生成数字签名
使用私钥对哈希值签名,私钥需离线存储(如加密U盘),禁止明文传输,命令示例:bash# ECC签名(私钥private.key,签名输出firmware.sig) openssl pkeyutl -sign -in firmware.sha256 -inkey private.key -out firmware.sig -pkeyopt digest:sha256 # SM2签名 gmssl sm2 -sign -in firmware.sm3 -inkey sm2_private.key -out firmware.sig -
步骤4:打包发布
将"镜像+签名"打包,推荐两种格式:- 内嵌式:将签名附加到镜像末尾,镜像头部预留签名长度字段(如4字节标识签名大小);
- 分离式:签名单独存储为.sig文件,通过文件名关联(如firmware.bin + firmware.sig)。
2.2 传输层(安全传输:2个关键措施)
确保签名后的镜像在传输(如OTA下载、生产烧录)中不被篡改:
- 生产烧录:使用加密烧录工具(如J-Link Secure、ST-Link加密模式),禁止明文传输镜像;
- OTA更新:通过HTTPS/MQTTs传输,额外添加"时间戳+随机数"防止重放攻击(如每次OTA请求携带10分钟内的时间戳)。
2.3 签名验证层(设备端:核心落地环节)
验证逻辑需集成到设备安全启动流程中,从硬件信任根(BootROM)开始,逐级验证,确保每一步加载的软件都合法:
2.3.1 硬件信任根:烧录公钥哈希(不可逆操作)
设备生产阶段,需将"验证公钥的哈希值"烧录到OTP(一次性可编程存储)或eFuse中,防止公钥被篡改,步骤:
-
提取公钥并计算哈希:
bash# 提取ECC公钥并计算SHA-256哈希 openssl pkey -in private.key -pubout -outform DER | openssl dgst -sha256 -binary -out pubkey_hash.bin -
烧录到OTP/eFuse:
- STM32:使用STM32CubeProgrammer,选择"OTP"区域,烧录pubkey_hash.bin;
- ESP32:通过esptool.py,执行
espefuse.py burn_efuse PUB_KEY_HASH pubkey_hash.bin。
注意:OTP/eFuse烧录后不可逆,需提前验证公钥正确性!
2.3.2 多级安全启动:3阶段验证流程(代码示例)
以ARM Cortex-M设备为例,验证流程需从BootROM开始,逐级加载并验证BL0(一级引导)、BL1(二级引导)、APP(应用):
-
第一阶段:BootROM验证BL0(硬件固化,不可修改)
BootROM是芯片出厂时固化在ROM中的代码,负责初始化硬件并验证BL0:
c// 1. 从Flash加载BL0到RAM(地址0x20000000,大小0x10000) memcpy(0x20000000, 0x08000000, 0x10000); // 2. 读取BL0末尾的签名(假设签名大小64字节,BL0大小0x10000-64) uint8_t bl0_sign[64]; memcpy(bl0_sign, 0x08000000 + 0x10000 - 64, 64); // 3. 计算BL0哈希(排除签名部分) uint8_t bl0_hash[32]; sha256_hash(0x20000000, 0x10000 - 64, bl0_hash); // 4. 从OTP读取公钥哈希,验证BL0签名 uint8_t pubkey_hash[32]; otp_read(PUB_KEY_HASH_ADDR, pubkey_hash, 32); if (!ecc_verify(bl0_hash, bl0_sign, pubkey_hash)) { while(1); // 验证失败,死循环防止启动 } // 5. 验证通过,跳转到BL0执行 ((void (*)(void))0x20000000)(); -
第二阶段:BL0验证BL1/APP
BL0运行在安全模式(如ARM TrustZone的Secure World),逻辑与BootROM验证类似,需注意:
- 若支持OTA更新,需验证"新APP"的签名,再覆盖旧APP;
- 加入版本号检查(如APP头部存储版本号),防止回滚到低版本(存在漏洞的版本)。
避坑指南:验证过程中需禁用中断(防止侧信道攻击),密钥和哈希值需存储在RAM的安全区域(如STM32的Secure RAM),使用后立即清零(memset)。
三、硬件适配:不同平台实操要点
针对主流嵌入式平台,需注意硬件特性差异,避免通用方案导致的性能问题:
3.1 ARM Cortex-M系列(最常用)
- Cortex-M0/M0+(低功耗):仅支持ECC-192/SM2,需用汇编优化关键算法(如模乘),避免递归调用;
- Cortex-M3/M4:可启用DSP指令集加速,使用CMSIS-DSP库优化哈希计算;
- Cortex-M7:支持ECC-384,若带FPU,可加速大数运算(需开启编译器FPU支持)。
示例代码:Cortex-M4上使用硬件SHA加速(STM32H7):
c
#include "stm32h7xx_hal.h"
HASH_HandleTypeDef hhash;
// 硬件SHA-256计算
void hw_sha256(uint8_t *input, uint32_t len, uint8_t *output) {
HASH_InitTypeDef init = {0};
init.DataType = HASH_DATATYPE_8B;
HAL_HASH_Init(&hhash, &init);
HAL_HASH_Start(&hhash, input, len, output, 1000); // 1000ms超时
HAL_HASH_DeInit(&hhash);
}
3.2 RISC-V平台(国产芯片常用)
如平头哥玄铁C906、乐鑫ESP32-C6,需注意:
- 优先使用芯片自带加密指令(如玄铁的"密码扩展指令集");
- 若无硬件加速,使用RV32版本的microECC库,编译时添加
-march=rv32imc参数。
3.3 无硬件加密的低端芯片(如AVR/STM32F1)
需通过软件优化降低资源占用:
- 采用"分段验证":将大镜像分成1KB块,逐块验证哈希,避免占用过多RAM;
- 关闭不必要的功能:如microECC仅编译ECC-256签名/验证功能,禁用密钥交换功能。
四、安全防护:4个必做的抗攻击措施
仅实现签名验证还不够,需针对嵌入式设备的物理特性,添加抗攻击防护:
4.1 物理攻击防护(防剖片/探针)
- 烧录后禁用调试接口:通过熔丝位禁用JTAG/SWD(如STM32的DBGMCU_CR寄存器,设置DBG_STOP位);
- 采用防剖片封装:工业级设备可选择陶瓷封装或带金属屏蔽层的封装,避免芯片被物理剖片。
4.2 侧信道攻击防护(防功耗/时序分析)
- 代码层面:使用"常数时间算法",避免密钥相关的条件分支(如比较哈希时,不提前返回,需遍历所有字节);
- 硬件层面:若芯片支持(如LKT6850),启用功耗扰乱功能,或在加密运算时切换CPU频率(打乱功耗曲线)。
4.3 故障注入防护(防电压毛刺/激光)
- 电压监控:使用芯片内部ADC监控供电电压,若检测到异常波动(如0.5V骤降),立即清空密钥并复位;
- 时序校验:在验证签名时,记录运算时间,若时间过短(如正常100ms,异常20ms),判定为故障注入,停止启动。
4.4 OTA更新防护(防恶意升级)
- 双分区设计:设备划分"当前APP区"和"备用APP区",OTA时先将新APP写入备用区,验证通过后再切换分区;
- 回滚机制:若新APP验证失败,立即回滚到当前APP区,避免设备变砖。
五、测试验证:3类必做测试用例
系统落地后,需通过以下测试确保可靠性,避免上线后出现安全漏洞:
5.1 功能测试(验证防篡改有效性)
| 测试用例 | 预期结果 | 测试工具 |
|---|---|---|
| 正常镜像+正确签名 | 设备正常启动 | 示波器(观察启动时序) |
| 镜像篡改(修改1字节)+正确签名 | 验证失败,设备停留在BootROM阶段 | Hex编辑器(修改镜像) |
| 正常镜像+伪造签名 | 验证失败,设备死循环 | 自定义签名生成工具 |
5.2 性能测试(验证资源占用)
- 时间测试:记录签名验证时间(如BL0验证BL1的耗时),需满足设备启动要求(如工业设备启动时间<2秒);
- 资源测试:使用IDE的内存分析工具(如Keil的RTX RTOS View),确保RAM占用<10KB(Cortex-M3设备)。
5.3 安全测试(验证抗攻击能力)
- 侧信道测试:使用示波器采集加密运算时的功耗曲线,分析是否存在密钥相关的规律波动;
- 故障注入测试:通过电压毛刺发生器(如ChipWhisperer)注入异常电压,验证设备是否会错误通过验证。
六、生产部署:2个关键流程规范
批量生产时,需规范密钥管理和烧录流程,避免人为失误导致安全漏洞:
6.1 密钥管理流程(分级管理,避免泄露)
- 根密钥:生成设备公钥的根CA密钥,需离线存储在HSM中,禁止带出安全机房;
- 设备密钥:每个设备生成唯一的ECC/SM2密钥对,由根CA签名生成设备证书,烧录时通过加密通道传输到设备。
6.2 烧录流程规范(防止恶意镜像注入)
- 生产PC需安装杀毒软件,禁止接入互联网;
- 镜像和签名文件需通过加密U盘导入,禁止通过网络传输;
- 烧录后执行"验证测试":设备启动后,通过串口输出验证结果,只有验证通过的设备才能出厂。
七、落地工具链推荐
| 工具类型 | 推荐工具 | 适用场景 |
|---|---|---|
| 密码库 | microECC(ECC)、GMSSL(国密) | 轻量级设备 |
| 烧录工具 | STM32CubeProgrammer、esptool.py | STM32/ESP32设备 |
| 测试工具 | ChipWhisperer(侧信道)、J-Link | 安全测试、调试 |
| 编译工具链 | ARM GCC(Cortex-M)、RISC-V GCC | 跨平台编译 |
总结:3个核心落地原则
- 轻量化优先:嵌入式设备资源有限,避免过度设计,优先选择"硬件加速+轻量级库"的组合;
- 信任链闭环:从BootROM到APP,每一级都需验证,不能跳过任何环节(如只验证APP,不验证Bootloader);
- 安全左移:在设计阶段就考虑抗攻击措施,而非上线后补漏洞(如熔丝位配置、调试接口禁用需在生产阶段完成)。
通过以上步骤,可快速落地一套可靠的嵌入式软件数字签名系统,满足大多数场景的防篡改需求,若需更高安全等级(如金融设备),可进一步引入PUF(物理不可克隆函数)或SE(安全元件)模块。