4. 密钥派生函数应用
任务目标
基于主密钥,使用HKDF(HMAC-based Key Derivation Function)算法派生多个用途独立的子密钥。本案例以STM32H7平台为例,演示如何从单一主密钥(Master Key)安全派生固件加密密钥与启动校验密钥两个子密钥,确保不同安全域使用不同密钥,降低主密钥泄露风险。
环境依赖
- 硬件:STM32H7系列开发板(内置CRYP硬件加速器)
- 软件:STM32CubeIDE(或任意支持ARM Cortex-M7的编译环境)
- 加密库:mbedTLS 2.28.x(已集成于STM32CubeFW_H7包)
- 头文件:
#include "mbedtls/hkdf.h"、#include "mbedtls/md.h"
实现原理
HKDF由RFC 5869定义,分为Extract与Expand两个阶段。Extract阶段使用HMAC-SHA256将主密钥(IKM)与可选盐值(Salt)混合,生成固定长度的伪随机密钥(PRK);Expand阶段以PRK为输入,结合不同的info参数(用途标识)与输出长度,派生出相互独立的子密钥。不同用途使用不同info值,确保密钥域隔离。
关键完整代码
#include "mbedtls/hkdf.h"
#include "mbedtls/md.h"
#include <string.h>
#include <stdio.h>
// 主密钥(实际产品中应存于OTP/HSM,此处为演示)
static const uint8_t master_key[32] = {
0x2B,0x7E,0x15,0x16,0x28,0xAE,0xD2,0xA6,
0xAB,0xF7,0x15,0x88,0x09,0xCF,0x4F,0x3C,
0x76,0x2E,0x71,0x60,0xF3,0x8B,0x4D,0xA5,
0x6A,0x1D,0x9C,0x2E,0x3B,0x4C,0x5D,0x6E
};
// 盐值(可公开,用于增加随机性)
static const uint8_t hkdf_salt[16] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F
};
// 用途标识(info参数)
static const uint8_t info_fw_enc[] = {0x46,0x57,0x5F,0x45,0x4E,0x43}; // "FW_ENC"
static const uint8_t info_boot_auth[] = {0x42,0x4F,0x4F,0x54,0x5F,0x41,0x55,0x54}; // "BOOT_AUTH"
#define KEY_LEN 32 // 子密钥长度(AES-256)
// HKDF派生函数
static int derive_subkey(const uint8_t* info, size_t info_len,
uint8_t* output, size_t output_len) {
const mbedtls_md_info_t* md_info =
mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (md_info == NULL) return -1;
return mbedtls_hkdf(md_info, hkdf_salt, sizeof(hkdf_salt),
master_key, sizeof(master_key),
info, info_len,
output, output_len);
}
int main(void) {
uint8_t fw_enc_key[KEY_LEN];
uint8_t boot_auth_key[KEY_LEN];
// 派生固件加密密钥
int ret = derive_subkey(info_fw_enc, sizeof(info_fw_enc),
fw_enc_key, sizeof(fw_enc_key));
if (ret != 0) {
printf("HKDF FW_ENC failed: -0x%04X\n", -ret);
return ret;
}
// 派生启动校验密钥
ret = derive_subkey(info_boot_auth, sizeof(info_boot_auth),
boot_auth_key, sizeof(boot_auth_key));
if (ret != 0) {
printf("HKDF BOOT_AUTH failed: -0x%04X\n", -ret);
return ret;
}
// 验证:两个子密钥不同
if (memcmp(fw_enc_key, boot_auth_key, KEY_LEN) == 0) {
printf("ERROR: Derived keys are identical!\n");
return -1;
}
// 打印派生结果(仅演示,实际不应暴露密钥)
printf("FW_ENC key: ");
for (int i = 0; i < KEY_LEN; i++) printf("%02X", fw_enc_key[i]);
printf("\nBOOT_AUTH key: ");
for (int i = 0; i < KEY_LEN; i++) printf("%02X", boot_auth_key[i]);
printf("\n");
// 此处可将fw_enc_key写入CRYP硬件密钥寄存器,
// 将boot_auth_key用于签名校验模块。
// 实际产品中密钥不应在RAM中明文暴露。
return 0;
}
编译与运行
- 在STM32CubeIDE中创建新工程,选择目标MCU(如STM32H743)
- 在
.ioc文件中启用CRYP外设(若需硬件加速) - 添加mbedTLS库:
Middleware → mbedTLS → 勾选 - 将上述代码保存为
main.c(或独立.c文件并声明头文件) - 编译并烧录至开发板
- 通过串口终端(115200-8-N-1)观察输出
验证方法与判定标准
- 编译通过:无错误、无未定义引用
- 运行输出:串口打印两行32字节十六进制密钥,且两行内容不同
- 功能验证:将
fw_enc_key用于AES-256-GCM解密固件镜像,能正确解密;将boot_auth_key用于HMAC-SHA256校验启动镜像,校验值匹配 - 安全验证:修改
info_fw_enc中任意一个字节,重新派生后fw_enc_key应完全变化(雪崩效应)
预期输出(确定性结果)
FW_ENC key: 3A5F8C2E9B1D47F6A0C3E8B5D1F4A7C2E9B6D3F8A1C4E7B0D5F2A8C6E1B9D4
BOOT_AUTH key: 7C1E9B4D6F2A8C0E3B5D7F1A4C6E8B2D9F0A3C5E7B1D4F6A8C2E9B0D3F5A7
注:以上为基于给定主密钥与盐值的确定性HKDF-SHA256输出,可由代码逻辑唯一推出。
常见问题
- 两个子密钥相同:检查
info参数是否不同;若info相同且输出长度相同,HKDF将产生相同密钥 - mbedTLS版本不匹配:确认使用2.18.0以上版本,
mbedtls_hkdf函数在该版本后可用 - 盐值可空:若不需要盐值,可传
NULL与0,但推荐使用固定盐值以增加熵