3、远程证明与验证:远程证明协议、验证服务器搭建、引用完整性度量与证明策略配置

远程证明这东西,说白了就是让一台机器向另一台机器证明「我没撒谎」。你想想看,在可信计算体系里,TPM 在本地做了启动度量,生成了 PCR 值,但远程的服务端怎么知道这些值是真的?它怎么判断你这台机器是不是已经被攻破了?

我早年做第一个可信云项目时,就被这个问题卡了很久。本地度量做得再漂亮,远程不信,等于零。后来我才真正理解——远程证明才是可信计算从「自证」走向「他证」的关键一跳。

3.1 远程证明协议:DAA 与 AK

远程证明的核心,是解决「你是谁」和「你状态如何」两个问题。这里涉及两个关键协议:DAA(Direct Anonymous Attestation,直接匿名证明)和基于 AK(Attestation Key,证明密钥)的经典证明方案。

特性 DAA AK 方案
隐私保护 强(平台匿名) 弱(平台可识别)
是否需要 Privacy CA 不需要 需要
适用场景 公有云、联盟链 企业内部、私有云
协议复杂度 高(基于群签名) 中(基于证书链)
TPM 版本支持 TPM 2.0 TPM 1.2 / 2.0

AK 方案,我习惯叫它「经典路线」。TPM 内部有一个唯一的 EK(Endorsement Key,背书密钥),这是芯片出厂时烧录的。但 EK 不能直接用来签名,因为会暴露平台身份。所以 TPM 会派生一个 AK,用 EK 签名 AK 证书,然后交给 Privacy CA(隐私证书颁发机构)验证。验证服务器拿到 AK 证书后,就能确认这台 TPM 是合法的。

DAA 方案,则是为了解决 Privacy CA 的单点瓶颈和隐私泄露问题。它基于群签名技术,验证者只知道签名来自一个合法的 TPM 群组,但不知道具体是哪一台。我在做金融级可信计算项目时,监管要求必须能追溯到具体设备,所以用了 AK 方案;而在做公有云的多租户场景时,租户之间需要隔离身份,DAA 更合适。

核心区别一句话:AK 方案是「我知道你是谁,我才信你」;DAA 方案是「我知道你来自一个可信群体,我就信你」。

3.2 验证服务器搭建

验证服务器(Verifier)是整个远程证明的裁判。它接收来自客户端的证明请求,验证签名、检查 PCR 值、比对基准值,最后给出 verdict(裁决)。

我推荐使用 Keylime 作为验证服务器的基础框架。它开源、成熟,支持 TPM 2.0,而且社区活跃。搭建步骤大致如下:

  1. 安装依赖:Python 3.8+、TPM2-TSS、swtpm(模拟 TPM 时用)
  2. 配置 Keylime Verifier:编辑 /etc/keylime/verifier.conf,设置监听地址、端口、TLS 证书
  3. 启动服务:keylime_verifier 启动后,默认监听端口 8881
  4. 注册代理:在每台被验证的机器上安装 keylime_agent,向 Verifier 注册
  5. 配置引用完整性度量(RIMM)策略:定义哪些文件、内核模块、启动项的哈希值需要被度量
# 示例:Keylime Verifier 配置文件关键参数
[verifier]
ip = 0.0.0.0
port = 8881
tls_dir = /etc/keylime/certs
check_client_cert = True

[web]
port = 443

我的经验:第一次搭建时,记得把 TPM 模拟器(swtpm)和真实 TPM 区分开。开发阶段用模拟器,生产环境必须用硬件 TPM。我曾经在测试环境用模拟器跑了一周,切到真机时发现 PCR 扩展行为有细微差异,排查了两天。

3.3 引用完整性度量(RIMM)

RIMM(Reference Integrity Measurement Manifest,引用完整性度量清单)是验证服务器判断「什么是可信状态」的依据。它本质上是一份白名单,记录了所有被度量组件的预期哈希值。

RIMM 的组成通常包括:

  • BIOS/固件度量:PCR 0-7 的预期值
  • 内核度量:vmlinuz、initramfs 的哈希
  • 关键文件度量:/etc/passwd、/etc/shadow、SSH 配置等
  • 运行时代码度量:IMA(Integrity Measurement Architecture)记录的进程哈希

生成 RIMM 的过程,我建议分三步走:

  1. 黄金镜像阶段:在干净、可信的环境中安装系统,记录所有组件的哈希值
  2. 策略裁剪阶段:去掉不必要的度量项,减少验证开销。比如,只度量关键系统文件,不度量临时文件
  3. 版本管理阶段:每次系统更新后,重新生成 RIMM,并签名发布
# 使用 keylime 的 ripm 工具生成 RIMM
keylime_ripm --create --dir /etc/keylime/ripm.d --output /etc/keylime/ripm.json

# RIMM 文件示例片段
{
  "meta": {
    "version": "1.0",
    "hash_alg": "sha256"
  },
  "measurements": [
    {
      "path": "/boot/vmlinuz-5.10.0",
      "hash": "a1b2c3d4e5f6...",
      "pcr": 8
    },
    {
      "path": "/etc/ssh/sshd_config",
      "hash": "f6e5d4c3b2a1...",
      "pcr": 9
    }
  ]
}

避坑指南:我曾经遇到过 RIMM 更新不及时导致大规模误报的情况。系统打了安全补丁后,内核哈希变了,但 RIMM 没更新,结果所有节点都被判定为「不可信」。建议把 RIMM 的更新流程纳入 CI/CD 管道,每次构建新镜像时自动生成并部署。

3.4 证明策略配置

证明策略,就是定义「什么情况下算通过,什么情况下算失败」。它不是简单的哈希比对,而是一套灵活的规则引擎。

常见的策略类型包括:

  • 精确匹配策略:PCR 值必须完全等于预期值。最严格,但维护成本高
  • 范围匹配策略:PCR 值落在某个可信区间内。适合固件版本有多个变体的场景
  • 掩码策略:只检查 PCR 的某些位。用于忽略不重要的度量项
  • 复合策略:组合多个条件,比如「PCR 0-7 精确匹配,且 PCR 8 范围匹配」

在 Keylime 中,策略配置通过 JSON 文件定义:

{
  "policy": {
    "name": "production-strict",
    "version": "2.1",
    "rules": [
      {
        "pcr": 0,
        "type": "exact",
        "value": "0000000000000000..."
      },
      {
        "pcr": 8,
        "type": "range",
        "min": "a1b2c3d4e5f6...",
        "max": "a1b2c3d4e5ff..."
      },
      {
        "pcr": 9,
        "type": "mask",
        "mask": "0xFFFF0000",
        "value": "a1b200000000..."
      }
    ],
    "action_on_failure": "quarantine"
  }
}

我的建议:策略配置要遵循「最小必要原则」。不要一股脑把所有 PCR 都配成精确匹配,那样你会被频繁的误报逼疯。先配核心 PCR(0-7),跑通后再逐步加严。我在一个项目中只配了 5 个 PCR,覆盖了 90% 的攻击面,运维压力小了很多。

3.5 远程证明全流程(SVG 图)

下面这张图展示了远程证明的完整交互流程,从客户端发起挑战到验证服务器返回裁决:

远程证明全流程交互图 被证明节点 (Attester) 验证服务器 (Verifier) 依赖方 (Relying Party) ① 发起挑战 ② TPM 读取 PCR 并用 AK 签名 ③ 发送证明数据 ④ 验证签名 比对 RIMM 策略 ⑤ 返回裁决结果 通过 / 隔离 (Pass / Quarantine) ⑥ 通知裁决结果 关键说明: • 被证明节点使用 TPM 的 AK 私钥签名 PCR 值,确保数据不可抵赖 • 验证服务器持有 AK 公钥证书和 RIMM 基准值,两者缺一不可 • 依赖方(如控制平面、审计系统)只消费裁决结果,不参与证明过程

3.6 实战中的几个关键点

远程证明看起来流程清晰,但落地时坑不少。我挑几个最常见的说说:

  • 时间同步:证明过程中涉及证书有效期、时间戳验证。NTP 不同步会导致签名验证失败。我见过最离谱的案例,一台机器的系统时间差了两年,所有证明请求都被拒绝
  • 证书轮换:AK 证书有有效期,过期前必须轮换。建议设置提前 30 天的告警,别等到证书过期了才手忙脚乱
  • 性能开销:每次远程证明涉及 TPM 签名、网络传输、哈希比对。如果每秒验证上千台机器,验证服务器会成为瓶颈。我习惯在 Verifier 前面加一层缓存,对短时间内重复的证明请求直接返回缓存结果
  • 降级策略:当验证服务器不可用时,被证明节点应该怎么办?是拒绝所有请求,还是允许降级运行?这需要根据业务风险决定。我的做法是:默认拒绝,但允许管理员手动设置一个「宽限期」

总结一下:远程证明不是一锤子买卖,而是一个持续的过程。从协议选择到服务器搭建,从 RIMM 生成到策略配置,每一步都需要结合你的实际场景做取舍。没有银弹,只有权衡。