3. 使用RSA签名验证固件更新包
任务说明
固件更新机制若仅依赖校验和或CRC,攻击者可轻易构造恶意固件并重放。本案例实现基于RSA-2048/SHA-256的数字签名验证流程:固件更新包由持有私钥的发布方签名,设备端使用预置公钥验证签名,确保更新包来源可信且内容未被篡改。验证通过后才允许写入Flash,否则拒绝更新。
环境与依赖
使用C语言配合mbedTLS库(v2.28或v3.x均可),在Linux主机模拟验证逻辑。实际嵌入时需将mbedtls移植至目标MCU,并确保公钥以只读方式存储在安全区域(如OTP或受保护的Flash分区)。
核心实现
#include <stdio.h>
#include <string.h>
#include "mbedtls/pk.h"
#include "mbedtls/sha256.h"
#include "mbedtls/rsa.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
// 设备端预置公钥(PEM格式,由私钥导出)
static const char DEVICE_PUBLIC_KEY[] =
"-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1Ih3pLmPzJ0nJxVY6nQ\n"
"..." // 实际使用时替换为完整公钥
"-----END PUBLIC KEY-----\n";
// 固件包结构:头部(4字节长度) + 固件数据 + 签名(256字节)
typedef struct {
uint32_t fw_size;
uint8_t *fw_data;
uint8_t signature[256];
} firmware_package_t;
static int verify_firmware_signature(const firmware_package_t *pkg) {
mbedtls_pk_context pk;
mbedtls_sha256_context sha_ctx;
unsigned char hash[32];
int ret;
mbedtls_pk_init(&pk);
mbedtls_sha256_init(&sha_ctx);
// 1. 加载预置公钥
ret = mbedtls_pk_parse_public_key(&pk,
(const unsigned char *)DEVICE_PUBLIC_KEY,
sizeof(DEVICE_PUBLIC_KEY));
if (ret != 0) {
printf("公钥解析失败: -0x%04X\n", -ret);
goto cleanup;
}
// 2. 计算固件数据的SHA-256哈希
mbedtls_sha256_starts(&sha_ctx, 0);
mbedtls_sha256_update(&sha_ctx, pkg->fw_data, pkg->fw_size);
mbedtls_sha256_finish(&sha_ctx, hash);
// 3. RSA公钥验签(PKCS#1 v1.5填充)
ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA256,
hash, sizeof(hash),
pkg->signature, sizeof(pkg->signature));
if (ret == 0) {
printf("签名验证通过:固件来源可信且未被篡改\n");
} else {
printf("签名验证失败: -0x%04X\n", -ret);
}
cleanup:
mbedtls_sha256_free(&sha_ctx);
mbedtls_pk_free(&pk);
return ret;
}
// 模拟固件更新流程
int main() {
// 实际固件数据(此处用示例数据模拟)
uint8_t fw[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
firmware_package_t pkg;
pkg.fw_size = sizeof(fw);
pkg.fw_data = fw;
// 签名由发布方离线生成,此处仅演示验证流程
// 实际签名值需由私钥对固件哈希签名后填入
memset(pkg.signature, 0, sizeof(pkg.signature));
int ret = verify_firmware_signature(&pkg);
if (ret == 0) {
// 验证通过后才执行Flash写入操作
printf("允许写入Flash...\n");
// flash_write(pkg.fw_data, pkg.fw_size);
} else {
printf("拒绝更新:固件包无效\n");
}
return ret;
}
编译与运行
# 编译(假设mbedtls已安装至/usr/local)
gcc -o fw_verify fw_verify.c -I/usr/local/include -L/usr/local/lib -lmbedtls -lmbedcrypto -lmbedx509
# 运行
./fw_verify
验证方法与判定标准
正向验证:使用发布方私钥对固件数据生成签名,填入pkg.signature后运行,程序输出“签名验证通过”并继续执行Flash写入逻辑。
反向验证:篡改固件数据任意一个字节(如修改fw[2]的值),或使用错误公钥签名,程序应输出“签名验证失败”并拒绝更新。
判定标准:签名验证函数返回0时执行写入,非0时终止更新流程。上述正反向测试结果与预期一致即通过。
关键说明
- 签名生成需在离线安全环境完成,私钥永不进入设备端。生成命令示例:
openssl dgst -sha256 -sign private.pem -out sig.bin firmware.bin - 实际产品中应将
DEVICE_PUBLIC_KEY编译进Bootloader或安全固件区域,防止被应用层覆盖。 - 本示例使用PKCS#1 v1.5填充,若使用PSS填充需将
mbedtls_pk_verify替换为mbedtls_rsa_rsassa_pss_verify并配置盐长度。