4. EdgeLock密钥管理与安全存储
在 MCX 系列 MCU 上,EdgeLock 安全子系统把密钥当作“受管资产”而不是普通数据:密钥一旦进入密钥库(Key Store),就不再以明文形式出现在应用可读的总线或内存中,所有加解密、签名、HMAC 操作都通过句柄引用密钥。理解这一点的关键,是先把密钥的“层次”和“派生关系”理清,再理解密钥保护模式如何决定密钥能否被导出、能否被派生、能否被硬件独占使用。
一、密钥层次:从设备根到会话密钥
MCX 的 EdgeLock 密钥体系可以抽象为四层。最底层是设备根密钥,它由芯片在制造阶段写入的一次性可编程区域(OTP/eFuse)与硬件唯一密钥(HUK)共同构成,不可读出、不可修改,是整个信任链的锚点。第二层是密钥加密密钥(KEK),通常由根密钥派生或由安全导入流程写入,用于保护下层密钥的机密性。第三层是应用主密钥,例如用于安全启动验证的 RSA/ECC 公钥、用于安全通信的对称主密钥。第四层是会话密钥/工作密钥,由主密钥按用途派生,生命周期短,用完即弃。
这种分层带来两个直接好处:一是上层密钥泄露不会直接暴露根密钥;二是可以通过“只派生、不导出”的方式,让密钥在硬件内部完成运算,应用只拿到结果。
二、密钥派生:从主密钥到用途隔离
密钥派生不是“再生成一个随机数”,而是用确定性算法从已有密钥和上下文信息推导出新的密钥。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 不匹配,导入立即失败,不会留下半写入状态。
五、MCX 密钥存储实战:从创建到使用
下面以一个“安全通信主密钥 + 会话密钥派生”的场景,给出可参考的操作流程。假设使用 MCX 的 EdgeLock 密钥库,目标是在密钥槽 0x10 创建 AES-128 主密钥,保护模式为“不可导出、可派生”,然后派生会话密钥用于 AES-GCM 加密。
- 创建主密钥槽:调用
sss_key_store_allocate()申请槽位 0x10,设置属性SSS_KEY_ATTR_NO_EXPORT | SSS_KEY_ATTR_DERIVABLE,密钥类型为AES_128。 - 写入主密钥:如果主密钥由设备内部随机生成,调用
sss_key_store_generate();如果从外部导入,调用sss_key_store_import()并传入加密 blob。 - 派生会话密钥:调用
sss_kdf_derive_key(),父密钥句柄指向槽 0x10,Label 设为"AES-GCM-Session",Context 设为本次会话的随机 nonce,输出到临时槽 0x11。 - 执行加密:调用
sss_aes_gcm_encrypt(),密钥句柄用 0x11,传入明文、AAD、IV,得到密文和 16 字节认证标签。 - 清理会话密钥:调用
sss_key_store_free()释放槽 0x11,确保会话密钥不残留。
判断操作是否成功,可以观察三个信号:一是 API 返回 kStatus_SSS_Success;二是密钥槽状态寄存器中对应槽位显示“已占用且不可导出”;三是加密输出的认证标签在解密端能被正确验证。如果导入时 CMAC 校验失败,API 会返回 kStatus_SSS_Fail,此时应检查 KEK 是否匹配、blob 是否被截断或篡改。
最后需要强调的是,密钥管理不是一次性配置,而是贯穿设备生命周期的过程:产线注入、现场启动、运行时会话、固件升级、设备退役,每个阶段对密钥的可见性和生命周期要求都不同。MCX 的 EdgeLock 密钥库通过硬件隔离和策略属性,把这些要求落到每个密钥槽上,使“密钥不出硬件”成为可验证的工程事实,而不是一句口号。