2. 生成并存储设备唯一密钥

任务定义

在MCU HSM(硬件安全模块)应用中,设备唯一密钥是安全通信、固件签名校验和数据加密的基础。本案例目标是在HSM内部生成一个128位随机密钥,将其安全存储于HSM非易失性存储区(NVM),并提供读取接口供应用层在安全上下文中使用。要求密钥永不以明文形式出现在MCU主处理器内存中,且具备防止重复生成的保护机制。

硬件连接与HSM内部结构

HSM内部密钥生成与存储架构 MCU主处理器 应用代码 API调用层 无法直接访问密钥 HSM安全边界 TRNG 真随机数发生器 密钥生成逻辑 AES-128密钥 NVM存储区 密钥槽位0x01 API: hsm_generate_unique_key() / hsm_get_unique_key() 仅返回密钥句柄,不暴露密钥内容 API调用 生命周期:未生成 → 已生成(永久锁定) → 可读取句柄

代码实现

以下代码基于NXP S32K3系列HSM固件接口,使用C语言实现。核心逻辑包括:检查密钥是否已存在、调用HSM安全服务生成密钥、将密钥写入NVM指定槽位。

/* hsm_unique_key.c */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "hsm_api.h"      /* HSM安全服务API */
#include "hsm_nvm.h"      /* HSM非易失存储驱动 */

#define KEY_SLOT_ID      0x01u   /* NVM中密钥槽位编号 */
#define KEY_SIZE_BYTES   16u     /* AES-128密钥长度 */

/* HSM密钥生成状态 */
typedef enum {
    KEY_STATE_UNINIT = 0,
    KEY_STATE_GENERATED = 1,
    KEY_STATE_ERROR = 0xFF
} key_state_t;

static key_state_t key_state = KEY_STATE_UNINIT;

/**
 * @brief 检查密钥槽位是否已有有效密钥
 * @return 1=已存在, 0=未生成, -1=错误
 */
static int hsm_key_slot_occupied(void)
{
    uint8_t slot_status = 0;
    hsm_err_t err;
    
    err = hsm_nvm_read_status(KEY_SLOT_ID, &slot_status);
    if (err != HSM_OK) {
        return -1;
    }
    
    return (slot_status == 0xA5u) ? 1 : 0;  /* 0xA5为有效标志 */
}

/**
 * @brief 生成并存储设备唯一密钥
 * @return 0=成功, 1=密钥已存在, -1=失败
 */
int hsm_generate_unique_key(void)
{
    hsm_err_t err;
    uint8_t key_material[KEY_SIZE_BYTES];
    hsm_key_handle_t key_handle;
    
    /* 检查是否已生成,防止重复覆盖 */
    int occupied = hsm_key_slot_occupied();
    if (occupied < 0) {
        return -1;
    }
    if (occupied == 1) {
        key_state = KEY_STATE_GENERATED;
        return 1;  /* 密钥已存在,无需重新生成 */
    }
    
    /* 步骤1: 调用HSM安全服务生成随机密钥 */
    hsm_key_generate_input_t gen_input = {
        .key_type = HSM_KEY_TYPE_AES_128,
        .key_usage = HSM_KEY_USAGE_ENCRYPT | HSM_KEY_USAGE_DECRYPT,
        .key_lifetime = HSM_KEY_LIFETIME_PERSISTENT
    };
    
    err = hsm_key_generate(&gen_input, &key_handle);
    if (err != HSM_OK) {
        return -1;
    }
    
    /* 步骤2: 从HSM内部导出密钥材料(仅在HSM边界内传输) */
    err = hsm_key_export(key_handle, key_material, KEY_SIZE_BYTES);
    if (err != HSM_OK) {
        hsm_key_delete(key_handle);
        return -1;
    }
    
    /* 步骤3: 将密钥写入NVM槽位 */
    err = hsm_nvm_write_key(KEY_SLOT_ID, key_material, KEY_SIZE_BYTES);
    if (err != HSM_OK) {
        memset(key_material, 0, KEY_SIZE_BYTES);
        hsm_key_delete(key_handle);
        return -1;
    }
    
    /* 步骤4: 标记槽位为有效 */
    err = hsm_nvm_write_status(KEY_SLOT_ID, 0xA5u);
    if (err != HSM_OK) {
        memset(key_material, 0, KEY_SIZE_BYTES);
        hsm_key_delete(key_handle);
        hsm_nvm_erase_key(KEY_SLOT_ID);
        return -1;
    }
    
    /* 步骤5: 清理临时缓冲区并删除HSM内部临时句柄 */
    memset(key_material, 0, KEY_SIZE_BYTES);
    hsm_key_delete(key_handle);
    
    key_state = KEY_STATE_GENERATED;
    return 0;
}

/**
 * @brief 获取设备唯一密钥句柄(供加密/签名等操作使用)
 * @param handle 输出参数,密钥句柄
 * @return 0=成功, -1=失败
 */
int hsm_get_unique_key(hsm_key_handle_t *handle)
{
    if (handle == NULL) {
        return -1;
    }
    
    /* 检查密钥是否已生成 */
    if (key_state != KEY_STATE_GENERATED) {
        int occupied = hsm_key_slot_occupied();
        if (occupied != 1) {
            return -1;
        }
        key_state = KEY_STATE_GENERATED;
    }
    
    /* 从NVM加载密钥到HSM内部,返回句柄 */
    hsm_err_t err = hsm_nvm_load_key(KEY_SLOT_ID, handle);
    return (err == HSM_OK) ? 0 : -1;
}

/* 主函数示例:演示调用流程 */
int main(void)
{
    hsm_key_handle_t dev_key;
    int ret;
    
    /* 初始化HSM模块 */
    hsm_init();
    
    /* 首次启动:生成并存储唯一密钥 */
    ret = hsm_generate_unique_key();
    if (ret == 0) {
        printf("设备唯一密钥生成并存储成功\n");
    } else if (ret == 1) {
        printf("设备唯一密钥已存在,跳过生成\n");
    } else {
        printf("密钥生成失败\n");
        return -1;
    }
    
    /* 获取密钥句柄供后续安全操作使用 */
    ret = hsm_get_unique_key(&dev_key);
    if (ret == 0) {
        printf("密钥句柄获取成功,可用于AES加密/解密操作\n");
    } else {
        printf("密钥句柄获取失败\n");
        return -1;
    }
    
    /* 后续可使用dev_key进行安全操作,此处不再展开 */
    
    return 0;
}

编译与部署

本案例代码需在HSM固件开发环境中编译,并烧录至MCU的HSM核。具体步骤如下:

  1. 将上述代码保存为hsm_unique_key.c,加入HSM固件工程;
  2. 链接HSM安全服务库(hsm_api.h对应实现)和NVM驱动库(hsm_nvm.h对应实现);
  3. 使用S32 Design Studio或IAR for ARM编译生成HSM固件镜像;
  4. 通过调试器将固件烧录至HSM专用Flash区域。

验证方法

完成烧录后,通过串口或调试器观察主函数输出,验证标准如下:

  • 首次运行:串口输出“设备唯一密钥生成并存储成功”,随后输出“密钥句柄获取成功”;
  • 复位后再次运行:输出“设备唯一密钥已存在,跳过生成”,证明密钥持久化存储生效;
  • 安全属性验证:通过调试器检查MCU主处理器内存,无法找到16字节连续明文密钥数据;
  • 防重复验证:连续调用hsm_generate_unique_key()两次,第二次返回1且不改变原密钥内容。

判定标准:以上四项全部满足,且密钥槽位状态标志为0xA5,即可判定本案例实现通过。