基于华为云 QingTian TPM 2.0 + K8s 的机密计算 Demo,展示医疗数据在"可信执行环境"中端到端加密处理的完整链路。
用户通过浏览器咨询健康问题,敏感的健康数据(症状描述)在浏览器端通过 HPKE 加密后传输,只有运行在可信 TEE(K8s Pod 内有 TPM 保护的环境)中的服务才能解密并处理。
若服务运行在不具有 TPM 保护的 Pod 上,则无法解密,请求失败。
Browser (React) K8s Pod (Backend+LLM) Huawei Cloud ECS
┌─────────────────────┐ ┌──────────────────────────┐ ┌────────────────┐
│ │ │ │ │ │
│ 1. GET /attestation│────────▶│ /attestation │────────▶│ /dev/tpm0 │
│ (fetch PCR) │ │ → go-tpm read PCR │ │ (QingTian TPM)│
│ │◀────────│ → return PCR1/4 │◀────────│ │
│ 2. Compare PCR │ │ │ │ │
│ vs golden │ │ /key-exchange │ │ │
│ 3. HPKE key gen │────────▶│ → DH key exchange │ │ │
│ (X25519) │ │ → return sessionId │ │ │
│ │◀────────│ │ │ │
│ 4. POST /chat │ │ /chat │ │ │
│ (encrypted) │────────▶│ → look up sessionId │ │ │
│ │ │ → decrypt with HPKE │ │ │
│ │ │ → call LLM │ │ │
│ │ │ → encrypt response │ │ │
│ │◀────────│ │ │ │
│ 5. decrypt & │ │ │ │ │
│ display │ │ │ │ │
└─────────────────────┘ └──────────────────────────┘ └────────────────┘
| 层 | 技术 | 说明 |
|---|---|---|
| 前端 | React + Next.js + TypeScript | 现有前端代码,不变 |
| 后端 | Go + go-tpm + go-tpm-tools |
新建 Go 服务 |
| TPM | 华为云 QingTian TPM 2.0 | 通过 /dev/tpm0 访问 |
| 加密 | HPKE (X25519 + HKDF-SHA256 + AES-128-GCM) | 前端用 @hpke/core,后端用 golang.org/x/crypto |
| LLM | llama.cpp server (qwen3.5-0.8B) | 端口 8080 /v1/chat/completions |
| K8s | K8s + device plugin | Pod 需挂载 /dev/tpm0 |
前端主动拉取 PCR 值,用于验证后端是否运行在可信环境。
请求: 无需参数
响应:
{
"pcrs": { "1": "sha256:abc123...", "4": "sha256:def456..." },
"publicKey": "base64-encoded-X25519-public-key",
"mock": false
}前端收到后,与预设的黄金基线比对:
- 一致 → 可信,继续密钥协商
- 不一致 → 拒绝通信
前端生成临时 DH 密钥对,把公钥发过来,完成 HPKE 密钥协商。
请求:
{
"clientPublicKey": "base64-encoded-X25519-ephemeral-public-key"
}响应:
{
"sessionId": "uuid-v4-session-id",
"enc": "base64-ephemeral-public-key",
"serverPublicKey": "base64-encoded-X25519-public-key"
}后端根据 sessionId 维护 sessionId → responseKey 的映射,用于后续解密和加密响应。
加密对话接口,所有数据都用 HPKE 加密。
请求:
{
"sessionId": "uuid-v4-session-id",
"enc": "base64-ephemeral-public-key",
"ct": "base64-iv-plus-ciphertext"
}响应: Streamable HTTP(application/x-ndjson),每行一个加密 chunk:
{"iv":"base64_iv","ct":"base64_ct"}
{"iv":"base64_iv","ct":"base64_ct"}
前端拉取 PCR → 对比黄金基线 → 通过则继续 → Key Exchange → Chat
黄金基线是什么?
PCR1:Bootloader + OS loader 的哈希PCR4:Bootloader
这些值在首次部署时从可信实例上获取,配置到前端 Deployment 环境变量中。任何启动链篡改都会导致 PCR 值变化,前端拒绝通信。
不需要:TPM 签名验证、证书链验证、EventLog 重放。
tpm-app/
├── README.md # 本文档
│
├── frontend/ # 现有 Next.js 前端(待改造指向 Go backend)
│ ├── src/
│ │ ├── app/
│ │ ├── components/
│ │ └── lib/
│ ├── package.json
│ └── ...
│
├── backend/ # 新建 Go 后端
│ ├── main.go # HTTP server,3个接口
│ ├── go.mod
│ ├── tpm/
│ │ ├── pcr.go # PCR 读取
│ │ └── key.go # HPKE 密钥对管理
│ ├── hpke/
│ │ └── hpke.go # HPKE 加解密(X25519 + AES-128-GCM)
│ ├── session/
│ │ └── manager.go # sessionId → responseKey 映射
│ └── llm/
│ └── client.go # LLM 流式调用
│
│ ├── docs/
│ │ ├── build-guide.md # Docker 构建指南
│ │ ├── docker-guide.md # Docker 使用指南
│ │ └── k8s-deployment.md # K8s 部署指南
│
│ ├── Dockerfile.llm # LLM 镜像构建
│ ├── Dockerfile.backend # 后端镜像构建
│ └── Dockerfile.frontend # 前端镜像构建
| 变量 | 默认值 | 说明 |
|---|---|---|
PORT |
8000 |
HTTP 服务端口 |
PCR_INDICES |
mock |
要读取的 PCR 索引,逗号分隔,如 1,4,7;空或 mock 为模拟模式 |
PCR_GOLDEN_BASELINE |
空 | 格式 1:sha256:abc...,4:sha256:def...;可选,后端仅记录日志 |
TPM_DEVICE |
/dev/tpm0 |
TPM 设备路径 |
KEY_PATH |
/var/lib/backend/enclave-key.json |
公钥持久化路径 |
LLM_BASE_URL |
http://localhost:8080 |
LLM 服务地址 |
LLM_MODEL |
qwen3.5 |
模型名称 |
| 变量 | 说明 |
|---|---|
NEXT_PUBLIC_API_BASE |
后端服务地址,如 http://backend-svc:8000 |
PCR_GOLDEN_BASELINE |
黄金基准线,格式同 backend,Next.js 服务端运行时读取用于前端独立校验 |
注意:
PCR_GOLDEN_BASELINE是服务端变量,前端通过 Next.js API route 运行时读取,不依赖构建时NEXT_PUBLIC_*。
- ECS 创建时需选择启用 QingTian TPM 的规格
- 镜像需是 UEFI 启动模式
- 切换规格时,目标规格也必须支持 TPM
- EventLog 上限 64KB,超过会导致引导失败