在智能硬件全生命周期管理体系中,固件安全防护构成了设备可信运行的核心屏障。从抵御恶意篡改引发的功能性安全漏洞,到防范未经授权的固件复制导致知识产权流失,均亟需构建系统化的安全防护体系。
采用基于国密标准的 SM2 非对称密码算法,融合物理不可克隆函数(PUF)技术,已成为当前智能硬件固件安全防护领域的前沿解决方案。
本文以 JC100 安全芯片为例,使用 SM2 算法与 PUF 模块协同工作,完整呈现从密钥生成、固件加密到完整性验证的全流程技术实现方案,为智能硬件开发人员提供具备工程实践价值的技术参考。
环境准备
在开始前,需准备好以下硬件和软件环境,确保后续开发顺利进行:
硬件方面:
JC100 安全芯片开发板
智能硬件原型
软件方面:
JC100 芯片 SDK
固件打包工具
在完成 SDK 安装后,通过专业调试设备将开发板接入系统,于 SDK 配置文件中精准指定目标芯片型号为 JC100,并同步启用物理不可克隆函数(PUF)及国密 SM2 算法的硬件模块驱动支持。
配置完成后,可执行标准化验证流程,通过调用芯片唯一识别码读取接口进行功能验证,若该接口能够稳定返回正确的芯片 ID 数据,则表明整个开发环境已完成初始化并达到可用状态。
调用 JC100 的 PUF 模块生成 PUF 根值
PUF(物理不可克隆函数)的核心价值在于,它能利用芯片制造过程中产生的物理特性差异,生成唯一且不可克隆的 “根值”—— 这意味着即使是同一批次的芯片,其 PUF 根值也完全不同,从源头避免了密钥被批量复制的风险。
在 JC100 中调用 PUF 模块生成根值的步骤如下:
初始化 PUF 模块:需先解锁芯片的 PUF 安全配置区(通过专用指令,防止未授权访问),然后调用 PUF_Init() 函数初始化模块,该函数会自检 PUF 电路状态,返回 PUF_STATUS_OK 表示初始化成功。
生成根值:调用 PUF_GenerateRoot() 函数,函数会通过 PUF 电路生成 256 位根值,并存储在临时缓冲区中。需要注意的是,根值生成过程是 “一次性” 的 —— 生成后不会再次产生相同值,因此必须立即安全存储。
安全存储根值:将生成的根值写入 JC100 内部的 “安全存储区”(该区域仅能通过芯片内部指令访问,外部无法直接读取),调用 SecureStore_Write(PUF_ROOT_ADDR, root_value, 32) 完成存储,其中 PUF_ROOT_ADDR 是芯片预定义的安全存储地址。
示例代码如下:
#include <stdint.h>
#include 「puf_module.h」
#include 「secure_store.h」
int main(void) {
// 初始化 PUF 模块
PUF_Status puf_status = PUF_Init();
if (puf_status != PUF_STATUS_OK) {
// 处理初始化失败
return -1;
}
// 生成 PUF 根值(256 位,32 字节)
uint8_t puf_root[32] = {0};
puf_status = PUF_GenerateRoot(puf_root, sizeof(puf_root));
if (puf_status != PUF_STATUS_OK) {
// 处理根值生成失败
return -1;
}
// 存储根值到安全存储区
if (SecureStore_Write(PUF_ROOT_ADDR, puf_root, 32) != STORE_SUCCESS) {
// 处理存储失败
return -1;
}
return 0;
}
提示:PUF 根值生成后若未及时存储,断电后会丢失,因此必须在生成后立即执行存储操作;且安全存储区的访问需严格控制权限,避免根值被意外读取。
用根值通过 KDF 生成 SM2 私钥
PUF 根值虽唯一,但直接作为密钥使用灵活性不足。通过密钥派生函数(KDF),可基于根值生成符合 SM2 算法要求的私钥 ——KDF 能在根值的基础上,结合上下文信息(如设备 ID、时间戳)生成不同场景下的密钥,既保证密钥与根值的关联性,又增强了密钥的多样性。
在 JC100 中,推荐使用 SM3-KDF,具体步骤如下:
准备 KDF 输入:输入包括 PUF 根值、上下文信息、派生密钥长度。
调用 KDF 生成私钥:通过 SDK 中的 SM3_KDF()函数,将输入参数传入,函数会输出 32 字节的私钥。需注意,上下文信息需明确区分不同设备或场景,避免不同设备派生相同私钥。
安全存储私钥:私钥作为核心机密,需存储在 JC100 的 “密钥保护区”(该区域支持硬件加密存储,仅允许 SM2 算法模块调用,无法直接读取)。
示例代码如下:
// 读取安全存储区中的 PUF 根值
uint8_t puf_root[32] = {0};
if (SecureStore_Read(PUF_ROOT_ADDR, puf_root, 32) != STORE_SUCCESS) {
return -1;
}
// 准备上下文信息(设备 ID+时间戳)
uint8_t context[] = 「DEV001_20250811」;
uint32_t context_len = sizeof(context) - 1; // 排除字符串结束符
// 调用 SM3-KDF 生成 SM2 私钥(32 字节)
uint8_t sm2_private_key[32] = {0};
if (SM3_KDF(puf_root, 32, context, context_len, 32, sm2_private_key) != KDF_SUCCESS) {
return -1;
}
// 将私钥存入密钥保护区
if (KeyStore_Write(SM2_PRIV_KEY_SLOT, sm2_private_key, 32) != KEYSTORE_SUCCESS) {
return -1;
}
由私钥计算出公钥
SM2 算法中,公钥由私钥通过椭圆曲线点乘运算生成,公式为公钥 = 私钥 * G(其中 G 为 SM2 曲线的基点)。在 JC100 中,该运算可通过硬件加速模块完成,既提高效率又避免私钥暴露在软件层面。
具体步骤:
读取私钥:从密钥保护区读取已存储的 SM2 私钥(注意:该操作仅能通过芯片内部函数完成,私钥不会暴露到外部内存)。
调用 SM2 公钥生成函数:通过 SM2_GeneratePublicKey() 函数,输入私钥,函数会利用硬件加速单元完成椭圆曲线点乘运算,输出 64 字节的 SM2 公钥(x 坐标 32 字节 + y 坐标 32 字节)。
公钥的使用与存储:公钥可公开存储(如写入设备的非易失性存储区),用于后续固件加密环节。
示例代码如下:
// 从密钥保护区读取私钥(内部操作,私钥不暴露)
uint8_t sm2_private_key[32] = {0};
if (KeyStore_Read(SM2_PRIV_KEY_SLOT, sm2_private_key, 32) != KEYSTORE_SUCCESS) {
return -1;
}
// 生成公钥
uint8_t sm2_public_key[64] = {0}; // x(32B) + y(32B)
if (SM2_GeneratePublicKey(sm2_private_key, sm2_public_key) != SM2_SUCCESS) {
return -1;
}
// 将公钥写入设备公开存储区
Flash_Write(PUBLIC_KEY_ADDR, sm2_public_key, 64);
固件加密
固件加密的核心是确保只有合法设备能解密并运行固件。这里采用 “混合加密” 方案:用对称密钥加密固件(效率高),再用 SM2 公钥加密对称密钥(安全性高),具体步骤如下:
生成随机对称密钥:使用 JC100 的硬件随机数生成器(TRNG)生成 128 位 AES 密钥(对称加密密钥),确保密钥的随机性。
加密固件:将固件二进制文件(如 firmware.bin)用 AES-CTR 模式加密,输入固件数据、AES 密钥和初始向量(IV),输出加密后的固件 encrypted_firmware.bin 。
加密对称密钥:用设备的 SM2 公钥对 AES 密钥进行加密(SM2 非对称加密),输出 64 字节的加密密钥 encrypted_aes_key 。
打包加密数据:将 encrypted_firmware.bin 、 encrypted_aes_key 和 IV 打包为最终的加密固件包 firmware_encrypted.pkg ,用于后续设备升级。
示例代码如下:
// 生成 128 位 AES 密钥
uint8_t aes_key[16] = {0};
if (TRNG_Generate(aes_key, 16) != TRNG_SUCCESS) {
return -1;
}
// 读取待加密的固件(假设已加载到内存 buffer)
uint8_t firmware_buffer[FIRMWARE_SIZE] = {0};
uint32_t firmware_len = FIRMWARE_SIZE;
read_firmware(「firmware.bin」, firmware_buffer, &firmware_len);
// 生成 AES-CTR 的初始向量(16 字节)
uint8_t iv[16] = {0};
TRNG_Generate(iv, 16);
// 用 AES-CTR 加密固件
uint8_t encrypted_firmware[FIRMWARE_SIZE] = {0};
AES_CTR_Encrypt(aes_key, 16, iv, firmware_buffer, firmware_len, encrypted_firmware);
// 用 SM2 公钥加密 AES 密钥
uint8_t sm2_public_key[64] = {0};
Flash_Read(PUBLIC_KEY_ADDR, sm2_public_key, 64);
uint8_t encrypted_aes_key[64] = {0}; // SM2 加密输出固定 64 字节
if (SM2_Encrypt(sm2_public_key, aes_key, 16, encrypted_aes_key) != SM2_SUCCESS) {
return -1;
}
// 打包加密数据
package_firmware(encrypted_firmware, firmware_len, encrypted_aes_key, iv, 「firmware_encrypted.pkg」);
固件验证
设备端在接收加密固件包后,需完成解密和验证,确保固件合法且完整。流程如下:
解析加密包:从 firmware_encrypted.pkg 中提取 encrypted_firmware.bin、 encrypted_aes_key 和 IV。
解密对称密钥:用设备内部存储的 SM2 私钥解密 encrypted_aes_key ,得到 AES 密钥。
解密固件:用 AES 密钥和 IV 解密encrypted_firmware.bin ,得到原始固件。
完整性验证:计算原始固件的 SM3 哈希值,与加密包中附带的哈希值比对 —— 若一致,说明固件未被篡改,可执行升级;否则拒绝升级。
示例代码如下:
// 解析加密固件包
uint8_t encrypted_firmware[FIRMWARE_SIZE] = {0};
uint32_t firmware_len = 0;
uint8_t encrypted_aes_key[64] = {0};
uint8_t iv[16] = {0};
uint8_t expected_hash[32] = {0}; // 包中附带的 SM3 哈希
parse_package(「firmware_encrypted.pkg」, encrypted_firmware, &firmware_len,
encrypted_aes_key, iv, expected_hash);
// 用 SM2 私钥解密 AES 密钥
uint8_t sm2_private_key[32] = {0};
KeyStore_Read(SM2_PRIV_KEY_SLOT, sm2_private_key, 32);
uint8_t aes_key[16] = {0};
if (SM2_Decrypt(sm2_private_key, encrypted_aes_key, aes_key, 16) != SM2_SUCCESS) {
return -1; // 解密失败,拒绝升级
}
// 解密固件
uint8_t firmware[FIRMWARE_SIZE] = {0};
AES_CTR_Decrypt(aes_key, 16, iv, encrypted_firmware, firmware_len, firmware);
// 验证完整性
uint8_t actual_hash[32] = {0};
SM3_Calculate(firmware, firmware_len, actual_hash);
if (memcmp(actual_hash, expected_hash, 32) != 0) {
return -1; // 哈希不一致,拒绝升级
}
// 验证通过,执行固件升级
update_firmware(firmware, firmware_len);
通过 JC100 安全芯片的 PUF 模块与 SM2 算法结合,我们实现了一套完整的智能硬件固件保护方案,实际开发中,开发者可根据硬件资源调整固件分块加密策略,或增加设备身份认证环节进一步提升安全性。若在实践中遇到具体问题,欢迎在评论区交流讨论。
关于珈港
珈港科技是科创板首批上市、国际领先的红外芯片企业睿创微纳旗下的安全芯片专业子公司,是国密 SM2 算法的第一发明人单位。
珈港科技总部位于山东烟台,在武汉、北京和深圳设有全资子公司。 依托国际一流水平的片上资产保护、密码算法和安全认证技术,珈港科技自主研发了一系列的安全 MCU、安全 SoC、物联网操作系统及云中间件等产品,为国内外客户提供先进的智能家居、工业控制和物联网解决方案。
245