refactor(android): 改用设备签名拦截器认证,移除 challenge 往返

设备接口统一由 AuthInterceptor 注入 Device-Sig 头完成认证,删除手动 challenge 签名流程与 transport-key 接口,注册改用本地生成 ts:nonce 做 PoP 验签。
This commit is contained in:
TongTongStudio
2026-08-27 08:43:02 +08:00
parent dbb75658a6
commit 586e5d9066
22 changed files with 600 additions and 1078 deletions

View File

@@ -62,19 +62,22 @@
### Phase 1设备注册 + 用户绑定
> **方案 AHeader 设备签名认证)**:设备不再先 `GET /api/device/challenge`。设备本地生成
> `ts:nonce`,用 TEE 私钥对 `METHOD|path|ts|nonce` 做 RSA-PSS 签名,写入请求头
> `Authorization: Device-Sig sn=...,ts=...,nonce=...,sig=...`;服务端 `DeviceAuthInterceptor`
> 统一验签(时效 + nonce 防重放 + 验签),业务接口零额外往返。仅注册接口用请求内公钥做 PoP 验签。
```
设备端 后端 用户端
│ │ │
│── 生成 TEE 密钥对 ──────────│ │
│── GET /api/device/challenge│
◄── {challenge} ────────────│
│── POST /api/device/register│ │
│ {sn,pubKey,challenge,sig}──►│── PoP 验签(challenge,sig) │
│── POST /api/device/register│ challenge=ts:nonce 由设备本地生成并自签名)
{sn,pubKey,challenge,sig}──►│── PoP 验签(用请求内公钥)
│ │── 存储 device(sn, pubKey) │
│◄── {deviceId} ─────────────│ │
│ │ │
│◄── POST /api/device/bind
│ {userId, sn}
POST /api/device/bind │ 每个设备接口自动携带
(Device-Sig 头 + {userId}) ──►│── DeviceAuthInterceptor 验签
│ │── 绑定 userId ↔ deviceId │
│ │◄── {ok} ────────────────────│
```
@@ -102,9 +105,7 @@
设备端(新) 后端 用户端
│ │ │
│── 新 TEE 密钥对 │ │
│── GET /api/device/challenge │
│◄── {challenge} ─────────────│ │
│── POST /api/device/register │ │
│── POST /api/device/register │ challenge=ts:nonce 本地自签名)
│ {sn,newPubKey,challenge,sig}►│PoP 验签 + 旧设备自动停用) │
│◄── {newDeviceId} ───────────│ │
│ │ │
@@ -372,14 +373,24 @@ TEE 密钥是「**门禁卡**」UK / DEK 才是「**保险柜钥匙**」。
### 设备端(无登录,信任边界 = TEE 密钥 / SN / Recovery Token
> 设备端所有接口除 `/api/device/register` 外,均由 `AuthInterceptor` 自动注入
> `Authorization: Device-Sig sn=...,ts=...,nonce=...,sig=...` 完成设备签名认证(方案 A
> 零 challenge 往返),请求体只含业务字段。
| Method | Path | 说明 |
|---|---|---|
| GET | `/api/device/status?sn=xxx` | 查询设备注册/绑定状态App 启动复用,免重复注册绑定) |
| POST | `/api/device/register` | 设备注册SN + 公钥 + PoP 验签 `{challenge, signature}`,幂等:同 SN 同公钥返回原 deviceId |
| POST | `/api/photo/upload` | 上传加密照片(设备签名验签 |
| POST | `/api/photo/recover` | 恢复后获取照片 DEK |
| POST | `/api/device/status` | 查询设备注册/绑定状态App 启动复用,免重复注册绑定) |
| POST | `/api/device/register` | 设备注册SN + 公钥 + PoP 验签 `{challenge, signature}`challenge 由设备本地生成 `ts:nonce`幂等:同 SN 同公钥返回原 deviceId |
| POST | `/api/device/bind` | 绑定设备(`{userId}`设备签名认证 |
| POST | `/api/device/sms/send` | 发送短信验证码(设备签名认证防轰炸) |
| POST | `/api/device/recover` | 恢复授权(短信 + SN 双因子,设备签名认证) |
| POST | `/api/device/photos` | 本设备绑定用户的照片 ID 列表 |
| POST | `/api/device/photos/metadata` | 本设备绑定用户的照片元数据 |
| POST | `/api/device/photo/{photoId}/local` | 设备本地下载解密单张照片(端到端加密) |
| POST | `/api/photo/upload` | 上传加密照片(设备签名 + 元数据验签) |
| POST | `/api/photo/recover` | 恢复后获取照片 DEK设备签名认证 |
> **注册/绑定幂等说明**`/api/device/register` 对「同一 SN + 相同公钥」幂等——App 重启后重复注册不会生成新 deviceId也不会误停用自己仅当公钥变化恢复出厂时才停旧换新。注册必须携带 PoP先 `GET /api/device/challenge`用 TEE 私钥对 challenge 签名,服务端验签通过才允许注册,防止攻击者用自己公钥冒名注册受害者 SNP0-1 修复)。App 启动时建议先调 `GET /api/device/status?sn=xxx`,若 `registered=true` 且 `bound=true`,直接复用返回的 `deviceId`/`userId`,跳过注册与绑定。
> **注册/绑定幂等说明**`/api/device/register` 对「同一 SN + 相同公钥」幂等——App 重启后重复注册不会生成新 deviceId也不会误停用自己仅当公钥变化恢复出厂时才停旧换新。注册必须携带 PoPchallenge 由设备本地生成 `ts:nonce`,用 TEE 私钥签名,服务端用请求内公钥验签),防止攻击者用自己公钥冒名注册受害者 SN。App 启动时建议先调 `POST /api/device/status`,若 `registered=true` 且 `bound=true`,直接复用返回的 `deviceId`/`userId`,跳过注册与绑定。
### 用户端(需要登录 Token