3. TPM与硬件信任根:TPM 2.0基础、平台配置寄存器(PCR)、密钥存储与管理
聊到可信计算,TPM 是个绕不开的硬件基础。我最早接触 TPM 是在做一款工控安全网关的时候,当时被客户要求必须支持 TPM 2.0 做密钥保护。说实话,刚开始看规范文档时头很大——几百页的 TCG 标准,全是英文。但真正上手后你会发现,核心概念其实就那么几个。
这一节,我们就把它拆开揉碎了讲。
3.1 TPM 2.0 到底是什么?
TPM,全称 Trusted Platform Module,说白了就是一个焊在主板上的安全芯片。它有自己的处理器、非易失性存储、随机数发生器,甚至还有独立的加密引擎。
TPM 2.0 相比 1.2 版本,最大的变化是:不再硬编码算法。1.2 时代只支持 SHA-1 和 RSA,到了 2.0,你可以灵活选择 SHA-256、ECC、SM3/SM4 等国密算法。这一点在实际项目中非常关键——我遇到过客户明确要求只能用国密,如果是 TPM 1.2 根本没法满足。
核心能力一览:
- 硬件级随机数生成
- 安全密钥生成与存储
- 平台完整性度量与报告
- 密封(Sealing)与绑定(Binding)
- 防篡改与防物理攻击
TPM 2.0 的架构分三层:命令层、接口层、设备层。上层应用通过 TSS(TCG Software Stack)发命令,TPM 芯片执行后返回结果。嗯,这里要注意:TPM 本身是被动设备,它不会主动做任何事,全靠软件驱动。
3.2 平台配置寄存器(PCR)——信任的锚点
PCR 是 TPM 里最巧妙的设计之一。你可以把它理解成一组只能扩展、不能重置的哈希寄存器(除了系统复位)。
扩展操作是这样的:
PCR_new = SHA256(PCR_old || new_measurement)
为什么只能扩展?因为只有这样,攻击者才无法伪造度量历史。我曾在一次安全审计中看到有人试图绕过 PCR 验证,结果发现只要 BIOS 阶段的度量值不对,后续所有 PCR 值都会错位——这就是链式信任的威力。
TPM 2.0 通常提供 24 个 PCR(编号 0-23),每个用途不同:
| PCR 编号 | 用途 | 度量内容 |
|---|---|---|
| 0 | BIOS/固件 | 主机平台固件代码 |
| 1 | 主机平台配置 | 主板配置数据 |
| 2 | Option ROM | 扩展卡固件 |
| 3 | Option ROM 配置 | 扩展卡配置数据 |
| 4 | MBR/引导加载器 | 主引导记录代码 |
| 5 | 引导加载器配置 | GRUB 等配置数据 |
| 6 | 状态转换/唤醒 | 休眠恢复状态 |
| 7 | 安全策略 | Secure Boot 策略 |
| 8-15 | 静态操作系统 | 内核、驱动等 |
| 16 | 调试 | 调试工具度量 |
| 17-23 | 动态/应用 | 应用层度量 |
你想想看,从按下电源键开始,BIOS 度量自己→度量 Option ROM→度量引导加载器→度量内核→度量应用,每一步的哈希值都扩展进 PCR。只要中间任何一环被篡改,最终 PCR 值就不对。这就是信任链的根基。
个人经验: 我在调试 UEFI Secure Boot 时,经常用 tpm2_pcrread 命令查看 PCR 值。如果 PCR 7(安全策略)的值和预期不符,十有八九是 Secure Boot 证书链出了问题。这个命令是排查信任链故障的第一把刀。
3.3 密钥存储与管理——TPM 的保险柜
TPM 2.0 的密钥管理,核心是树形结构。根节点叫 SRK(Storage Root Key),它由 TPM 内部生成,私钥永远不出芯片。SRK 下面挂载子密钥,子密钥下面再挂载孙密钥,形成一棵密钥树。
密钥类型主要有这几种:
- SRK(存储根密钥):树根,保护所有子密钥
- EK(背书密钥):芯片身份标识,出厂预置
- AK(认证密钥):用于平台身份证明
- SK(签名密钥):用于数字签名
- BK(绑定密钥):用于数据加密绑定
我曾经犯过一个错:在项目中直接把应用密钥存在文件系统里,用 TPM 加密后存储。后来发现,每次解密都要经过 TPM,性能开销很大。正确的做法是:用 TPM 生成一个对称密钥,然后用这个对称密钥去加密应用数据。这样既利用了 TPM 的安全性,又避免了频繁调用 TPM 的性能瓶颈。
避坑指南: 我曾经在批量部署时发现,不同厂商的 TPM 芯片对 SRK 的算法支持不一样。有的只支持 RSA-2048,有的支持 ECC-NIST P256。如果你在代码里硬编码了算法类型,换硬件平台就可能出问题。建议在初始化时先查询 TPM 的能力集,再动态选择算法。
3.4 核心流程:信任链与密钥树
为了让你更直观地理解,我画了一张图,把信任链和密钥树放在一起看:
这张图左边是信任链,右边是密钥树。两者通过 TPM 的密封(Sealing)操作关联起来——你可以把密钥密封到特定的 PCR 状态。只有 PCR 值匹配时,TPM 才允许解封密钥。这就是硬件信任根的完整闭环。
3.5 实操:TPM 2.0 基本操作示例
下面我用 tpm2-tools 演示几个常用操作。这些命令在 Ubuntu 20.04+ 上可以直接跑:
# 1. 查看 TPM 版本和厂商信息
tpm2_getcap properties-fixed
# 2. 读取 PCR 值(以 PCR 0 为例)
tpm2_pcrread sha256:0
# 3. 创建主密钥(SRK 下的子密钥)
tpm2_createprimary -C o -c primary.ctx
# 4. 创建签名密钥
tpm2_create -C primary.ctx -u key.pub -r key.priv
# 5. 加载密钥到 TPM
tpm2_load -C primary.ctx -u key.pub -r key.priv -c key.ctx
# 6. 用密钥签名
echo "hello tpm" | tpm2_sign -c key.ctx -o sig.bin
# 7. 密封数据到 PCR 状态
echo "secret data" | tpm2_seal -c primary.ctx -pcr sha256:0,4 -o sealed.dat
个人建议: 刚开始学 TPM 时,别急着写代码。先在命令行里把 tpm2-tools 玩熟,理解每个命令的输入输出。我当年就是靠反复敲这些命令,才真正搞懂了密钥树和 PCR 的关系。
3.6 常见陷阱与避坑
做 TPM 开发这些年,我踩过不少坑。挑几个典型的说说:
- PCR 值会变:系统更新 BIOS、更换硬件、修改 Secure Boot 策略,都会导致 PCR 值变化。密封数据时一定要考虑升级场景,否则解封会失败。
- TPM 有并发限制:TPM 芯片同一时间只能处理一个命令。高并发场景下需要排队,否则会返回 TPM_RC_RETRY。我曾在高吞吐量的签名服务中遇到这个问题,最后加了一层命令队列才解决。
- NV 索引空间有限:TPM 的非易失性存储是有限的,通常只有几十 KB。别把大文件往里塞,只存关键密钥和度量值。
- 模拟器与真芯片有差异:开发时可以用 swtpm 模拟器,但生产环境一定要用真芯片测试。我遇到过模拟器上跑得好好的,真芯片上某些命令返回不同错误码的情况。
曾经踩过的坑: 有一次我把 TPM 的 NV 索引写满了,结果系统启动时 TPM 初始化失败,整台机器起不来。最后只能拆机清空 TPM——嗯,从那以后我再也不敢滥用 NV 存储了。
TPM 2.0 是个好东西,但需要你理解它的设计哲学:信任链从硬件开始,密钥永远不出芯片。掌握了 PCR 和密钥树这两个核心概念,后面再讲远程证明、密钥轮换、告警联动时,你就不会觉得吃力了。