4. EdgeLock密钥管理与安全存储

在 MCX 系列 MCU 上,EdgeLock 安全子系统把密钥当作“受管资产”而不是普通数据:密钥一旦进入密钥库(Key Store),就不再以明文形式出现在应用可读的总线或内存中,所有加解密、签名、HMAC 操作都通过句柄引用密钥。理解这一点的关键,是先把密钥的“层次”和“派生关系”理清,再理解密钥保护模式如何决定密钥能否被导出、能否被派生、能否被硬件独占使用。

一、密钥层次:从设备根到会话密钥

MCX 的 EdgeLock 密钥体系可以抽象为四层。最底层是设备根密钥,它由芯片在制造阶段写入的一次性可编程区域(OTP/eFuse)与硬件唯一密钥(HUK)共同构成,不可读出、不可修改,是整个信任链的锚点。第二层是密钥加密密钥(KEK),通常由根密钥派生或由安全导入流程写入,用于保护下层密钥的机密性。第三层是应用主密钥,例如用于安全启动验证的 RSA/ECC 公钥、用于安全通信的对称主密钥。第四层是会话密钥/工作密钥,由主密钥按用途派生,生命周期短,用完即弃。

这种分层带来两个直接好处:一是上层密钥泄露不会直接暴露根密钥;二是可以通过“只派生、不导出”的方式,让密钥在硬件内部完成运算,应用只拿到结果。

MCX EdgeLock 密钥层次与派生关系 设备根密钥(HUK + OTP) 不可读出 · 不可修改 · 信任锚点 派生 / 保护 密钥加密密钥 KEK 保护下层密钥机密性 加密 / 导入 安全启动公钥 RSA-2048 / ECC P-256 安全通信主密钥 AES-128 / AES-256 设备身份密钥 ECC 签名密钥对 会话密钥(AES-GCM) 短生命周期 · 用完即弃 HMAC 会话密钥 消息完整性校验 ECDH 共享密钥 安全通道建立 硬件保护域 不可导出 · 不可读 可派生 不可导出 可导出 受策略约束

二、密钥派生:从主密钥到用途隔离

密钥派生不是“再生成一个随机数”,而是用确定性算法从已有密钥和上下文信息推导出新的密钥。MCX 的 EdgeLock 支持基于 NIST SP 800-108 的 KDF,常用模式是 Counter Mode 和 Feedback Mode。派生输入通常包括:父密钥句柄、标签(Label)、上下文(Context)、输出长度。标签用于区分用途,例如 "AES-GCM-Enc" 与 "HMAC-Auth" 必须使用不同标签,确保同一主密钥派生出的加密密钥和认证密钥互不相关。

一个典型的派生调用在 MCX 上通过 EdgeLock 固件 API 完成,伪代码如下:

// 假设已获得主密钥句柄 masterKeyHandle
// 派生 AES-GCM 会话密钥
sss_kdf_derive_key(
    &kdfCtx,
    masterKeyHandle,          // 父密钥句柄
    "AES-GCM-Enc",            // Label,用途隔离
    sessionNonce, 16,         // Context,绑定会话
    derivedKeyHandle,         // 输出:派生密钥句柄
    AES_128_KEY_SIZE          // 输出长度 16 字节
);
// 派生后,derivedKeyHandle 可直接用于 sss_aes_gcm_encrypt()
// 明文密钥不出硬件,应用只持有句柄

这里的关键判断是:派生密钥的句柄是否允许导出。如果策略设置为“不可导出”,那么即使应用被攻破,攻击者也只能调用该句柄执行运算,无法把密钥复制走。这正是 EdgeLock 与普通软件密钥管理的本质区别。

三、密钥保护模式:四种典型策略

MCX 密钥库中的每个密钥槽都带有一组属性,决定它的保护模式。常见组合如下表所示:

保护模式可读可导出可派生典型用途
明文可导出是是是开发调试、临时密钥
加密可导出否是(密文)是密钥备份、跨设备迁移
不可导出否否是安全启动公钥、设备身份密钥
硬件独占否否否根密钥、HUK 派生密钥

选择保护模式的原则是“最小权限”:能不可导出就不导出,能硬件独占就不允许派生。例如安全启动验证公钥只需要“不可导出 + 可读公钥部分”,而设备身份私钥必须“不可导出 + 不可读”,只允许签名操作。

四、安全密钥导入:把外部密钥安全地送进密钥库

实际生产中,密钥往往在外部 HSM 或产线服务器生成,需要导入 MCX。直接写明文密钥到 Flash 是不可接受的。MCX 支持基于 KEK 的密钥导入流程:外部系统用与设备共享的 KEK 加密目标密钥,生成密钥 blob;设备收到 blob 后,在 EdgeLock 内部用 KEK 解密,并直接写入指定密钥槽,全程明文不出硬件。

导入 blob 的典型结构包含:版本号、密钥类型、密钥长度、加密后的密钥数据、以及一个完整性校验标签(如 CMAC)。设备端导入时先校验 CMAC,再解密写入。如果 CMAC 不匹配,导入立即失败,不会留下半写入状态。

注意:KEK 本身必须通过安全通道注入,且每个设备的 KEK 应唯一。如果多台设备共用同一 KEK,一台设备被攻破就会威胁整个批次。产线应使用“一机一密”策略,KEK 由设备 HUK 派生或由产线安全服务器按设备 ID 单独加密下发。

五、MCX 密钥存储实战:从创建到使用

下面以一个“安全通信主密钥 + 会话密钥派生”的场景,给出可参考的操作流程。假设使用 MCX 的 EdgeLock 密钥库,目标是在密钥槽 0x10 创建 AES-128 主密钥,保护模式为“不可导出、可派生”,然后派生会话密钥用于 AES-GCM 加密。

  1. 创建主密钥槽:调用 sss_key_store_allocate() 申请槽位 0x10,设置属性 SSS_KEY_ATTR_NO_EXPORT | SSS_KEY_ATTR_DERIVABLE,密钥类型为 AES_128。
  2. 写入主密钥:如果主密钥由设备内部随机生成,调用 sss_key_store_generate();如果从外部导入,调用 sss_key_store_import() 并传入加密 blob。
  3. 派生会话密钥:调用 sss_kdf_derive_key(),父密钥句柄指向槽 0x10,Label 设为 "AES-GCM-Session",Context 设为本次会话的随机 nonce,输出到临时槽 0x11。
  4. 执行加密:调用 sss_aes_gcm_encrypt(),密钥句柄用 0x11,传入明文、AAD、IV,得到密文和 16 字节认证标签。
  5. 清理会话密钥:调用 sss_key_store_free() 释放槽 0x11,确保会话密钥不残留。

判断操作是否成功,可以观察三个信号:一是 API 返回 kStatus_SSS_Success;二是密钥槽状态寄存器中对应槽位显示“已占用且不可导出”;三是加密输出的认证标签在解密端能被正确验证。如果导入时 CMAC 校验失败,API 会返回 kStatus_SSS_Fail,此时应检查 KEK 是否匹配、blob 是否被截断或篡改。

实践建议:在开发阶段,可以先用“明文可导出”模式验证功能链路,确认派生、加密、解密逻辑正确后,再切换到“不可导出”模式做最终测试。切换后要重新验证所有依赖密钥导出的调试代码是否已移除,否则会出现“功能正常但密钥无法备份”的隐性依赖。

最后需要强调的是,密钥管理不是一次性配置,而是贯穿设备生命周期的过程:产线注入、现场启动、运行时会话、固件升级、设备退役,每个阶段对密钥的可见性和生命周期要求都不同。MCX 的 EdgeLock 密钥库通过硬件隔离和策略属性,把这些要求落到每个密钥槽上,使“密钥不出硬件”成为可验证的工程事实,而不是一句口号。