TPM 基础与配置:TPM 2.0 架构、EK/AK 与 PCR
聊远程证明,TPM 是绕不开的硬件根基。很多朋友刚开始接触时,容易被 TPM 内部那一堆术语搞晕——EK、AK、PCR、NV Index…… 说实话,我早年第一次看 TPM 2.0 规范时也头大。但后来我发现,只要抓住三个核心概念,整个远程证明的信任链条就清晰了:TPM 2.0 架构、EK/AK 密钥体系、PCR 机制。这一章,我们就把这三点彻底讲透。
TPM 2.0 架构:从“黑盒”到“分层”
TPM 1.2 像个黑盒子,功能固定,你改不了。TPM 2.0 不一样,它引入了分层架构,说白了就是“内核 + 库 + 应用”的思路。我个人觉得,理解这个分层是后续所有配置的基础。
TPM 2.0 内部主要分三层:
- TPM 设备驱动:最底层,负责和硬件通信。这部分你基本不用碰,操作系统帮你搞定了。
- TPM 资源管理器 (TBS):中间层,管理 TPM 的有限资源。TPM 硬件资源很金贵,比如同时只能处理一个会话,TBS 就是那个调度员。
- TPM 命令与响应:最上层,你通过工具(比如 tpm2-tools)发送命令给 TPM,TPM 返回结果。
嗯,这里要注意:TPM 2.0 的架构里,密钥、会话、对象都是“实体”,它们有生命周期。你创建了一个密钥,如果不显式保存或加载,TPM 重启后就没了。我在项目中遇到过好几次,同事抱怨“我的密钥怎么丢了”,其实就是没理解 TPM 的易失性存储机制。
核心要点:TPM 2.0 是“命令驱动”的。你通过发送命令来创建密钥、扩展 PCR、生成签名。所有操作都是显式的,没有“自动保存”这回事。
下面这张图,是我自己总结的 TPM 2.0 内部逻辑分层,你看一眼就能明白数据是怎么流动的:
EK(背书密钥)与 AK(证明密钥)
这两个密钥是远程证明的“身份证”和“工作证”。很多初学者容易混淆,我当年也犯过这个错。你想想看,EK 是 TPM 出厂时烧录的,全世界唯一,相当于 TPM 的“出生证明”。而 AK 是你自己生成的,用于日常的远程证明操作。
EK(背书密钥)
EK 是 TPM 的根信任。它是一对非对称密钥(RSA-2048 或 ECC),私钥永远不出 TPM 芯片。EK 的公钥会附带一个由 TPM 厂商签发的证书,叫 EK 证书。这个证书是远程证明中“验证对方 TPM 是否真实”的关键。
我的经验:在实际部署中,EK 证书的获取往往是第一个坑。有些厂商的 TPM 没有预置 EK 证书,或者证书格式不标准。我曾经在一个项目中,因为 EK 证书的 OID 字段解析失败,导致整个远程证明链路卡了两天。解决方案是:先通过 tpm2_getekcertificate 确认证书是否存在,如果不存在,需要从厂商官网手动下载并注入。
AK(证明密钥)
AK 是你自己创建的,用于签名 PCR 值或其他证明数据。AK 的私钥同样受 TPM 保护,但 AK 本身需要被 EK “背书”。这个过程叫 AK 认证(MakeCredential / ActivateCredential)。
AK 的生成流程,我建议你记住这四步:
- 创建 AK:使用
tpm2_createak命令,指定算法(RSA 或 ECC)。 - 获取 AK 公钥:TPM 返回 AK 公钥和它的名字(Name)。
- 生成 AK 证书:这一步通常由远程证明服务端完成——服务端用 EK 公钥加密一个“激活凭据”,TPM 用 EK 私钥解密,验证 AK 确实是在这个 TPM 里生成的。
- 保存 AK 句柄:AK 创建后是易失性的,你需要把它保存到 TPM 的持久化存储(NV Index)中,否则 TPM 重启后 AK 就丢了。
避坑指南:我曾经遇到过一个问题:AK 创建后,重启机器,AK 句柄失效了。原因是我没有把 AK 保存到 NV Index。记住,tpm2_evictcontrol 是必须的步骤。另外,AK 的名字(Name)是后续验证的关键,一定要妥善保存。
EK 和 AK 的关系,我用一个表格来对比,这样更清晰:
| 特性 | EK(背书密钥) | AK(证明密钥) |
|---|---|---|
| 生成方式 | TPM 厂商预置(出厂时) | 用户创建(运行时) |
| 私钥位置 | 永远在 TPM 内部 | 永远在 TPM 内部 |
| 用途 | 身份证明、解密激活凭据 | 签名 PCR 值、远程证明 |
| 证书 | EK 证书(厂商签发) | AK 证书(服务端签发) |
| 生命周期 | 永久(不可删除) | 可创建、可删除、可持久化 |
| 数量 | 通常 1 个 | 可创建多个 |
PCR(平台配置寄存器)
PCR 是 TPM 里最巧妙的设计之一。它本质上是一组只能扩展、不能重置(除重启外)的哈希寄存器。你想想看,如果 PCR 可以随意写入任意值,那远程证明就没意义了。正是这种“单向扩展”的特性,让 PCR 能记录系统的启动链。
PCR 的扩展操作很简单:PCR[n] = SHA256(PCR[n] || new_data)。也就是把当前 PCR 值和新的测量值拼接,再做一次哈希。这样,最终的 PCR 值就包含了所有历史测量信息的“指纹”。
TPM 2.0 规范定义了 24 个 PCR(0-23),每个 PCR 有特定的用途:
| PCR 索引 | 用途 | 测量内容 |
|---|---|---|
| 0-1 | BIOS/固件 | BIOS 代码、ACPI 表 |
| 2-3 | Option ROM | 扩展卡固件 |
| 4 | MBR/引导加载器 | GRUB 第一阶段 |
| 5 | 引导加载器配置 | GRUB 配置文件 |
| 6 | 状态转换/休眠 | 休眠恢复映像 |
| 7 | 安全策略 | Secure Boot 策略 |
| 8-9 | 操作系统内核 | 内核、initramfs |
| 10-15 | 操作系统组件 | 系统服务、模块 |
| 16-23 | 用户定义/调试 | 应用层测量 |
在实际的远程证明中,我们通常只关心 PCR 0-7(固件和引导链)和 PCR 8-11(内核和关键服务)。你想想看,如果攻击者篡改了 GRUB 配置文件,PCR 5 的值就会和预期值不同,远程证明服务端就能立刻发现。
关键理解:PCR 不是用来“存储数据”的,而是用来“记录测量历史”的。远程证明的核心逻辑就是:客户端报告 PCR 值,服务端比对“预期 PCR 值”。如果一致,说明系统没有被篡改。
嗯,这里还要提一个我踩过的坑:PCR 的“重置”时机。TPM 重启后,所有 PCR 会重置为初始值(全 0 或全 1,取决于规范)。但有些平台在 S3 休眠恢复后,PCR 不会重置。我曾经在一个项目中,因为没考虑到 S3 恢复场景,导致 PCR 值“看起来”没变,但实际系统已经换了状态。解决方案是:在远程证明前,先检查 TPM 的启动计数器(TPM2_GetCapability),确认系统是冷启动还是热启动。
最后,我用一张图来总结 PCR 的扩展过程和它在远程证明中的位置:
好了,TPM 的这三个核心概念——架构、EK/AK、PCR——是远程证明的基石。你理解了它们,后续的 Quote、验证、策略配置就顺理成章了。我个人建议,你可以在自己的实验环境里,用 tpm2_pcrread 和 tpm2_createak 实际动手操作一遍,感受一下 TPM 的“单向扩展”和“密钥绑定”到底是怎么回事。纸上得来终觉浅,绝知此事要躬行。