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并配置盐长度。