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 信任链与密钥树结构图 TPM 信任链与密钥树结构 信任链(PCR 扩展) BIOS 固件度量 → PCR[0] Option ROM 度量 → PCR[2] 引导加载器度量 → PCR[4] 内核度量 → PCR[8-15] 应用度量 → PCR[17-23] 密钥树(TPM 内部) SRK(存储根密钥) EK AK SK 子密钥 1 子密钥 2 子密钥 3 关键理解 左侧:信任链通过 PCR 扩展,确保启动过程未被篡改 右侧:密钥树以 SRK 为根,子密钥受父密钥保护,私钥永不离开 TPM

这张图左边是信任链,右边是密钥树。两者通过 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 和密钥树这两个核心概念,后面再讲远程证明、密钥轮换、告警联动时,你就不会觉得吃力了。