🔐 安全设备 Demo — Android + Spring Boot

场景:高权限设备端(无登录)+ 用户端(有登录体系)+ SN 绑定 + 私密照片加密
目标:设备端 TEE 密钥不可导出、服务端零知识、恢复出厂后可安全恢复


📐 架构总览

┌─────────────────────┐         HTTPS         ┌──────────────────────┐
│   Android 设备端     │ ◄──────────────────► │  Spring Boot 后端     │
│   (无登录/高权限)    │                      │  (用户有登录体系)      │
└─────────────────────┘                      └──────────────────────┘
         │                                            │
         │ ① TEE 密钥对Keystore                    │ ① 用户主密钥UK
         │ ② AES-GCM 信封加密照片                      │ ② DEK 信封加密存储
         │ ③ ECDSA 签名元数据                         │ ③ SN 绑定 + 短信验证
         │ ④ 恢复出厂 → 新密钥对                      │ ④ Recovery Token 下发
                                                   │ ⑤ UK 解 DEK → 设备公钥加密下发

🔑 密钥分层模型

┌─────────────────────────────────────────────────────────┐
│                                                         │
│   User Key (UK) — 用户主密钥                            │
│   ├── 生成用户注册时创建AES-256                    │
│   ├── 存储:服务端加密存储(生产环境用 KMS 托管)         │
│   └── 用途:加密所有 DEK                               │
│                                                         │
│   Data Encryption Key (DEK) — 每照片一个                │
│   ├── 生成:设备端 SecureRandom每次随机              │
│   ├── 加密UK 加密后存服务端                           │
│   └── 用途AES-256-GCM 加密照片                       │
│                                                         │
│   Device Key Pair — TEE 硬件密钥                        │
│   ├── 生成Android KeystoreStrongBox 优先)          │
│   ├── 私钥:永不导出,仅签名/解密                       │
│   ├── 公钥:上传服务端,用于加密下发                    │
│   └── 销毁:恢复出厂时自动清除                          │
│                                                         │
└─────────────────────────────────────────────────────────┘

🔄 完整流程

Phase 1设备注册 + 用户绑定

设备端                          后端                          用户端
  │                              │                              │
  │── 生成 TEE 密钥对 ──────────│                              │
  │── POST /api/device/register │                              │
  │   {sn, publicKey}          ──►│                              │
  │                              │── 存储 device(sn, pubKey)    │
  │◄── {deviceId} ─────────────│                              │
  │                              │                              │
  │                              │◄── POST /api/device/bind     │
  │                              │    {userId, sn}              │
  │                              │── 绑定 userId ↔ deviceId     │
  │                              │◄── {ok} ────────────────────│

Phase 2拍照 → 信封加密 → 上传

设备端                          后端
  │                              │
  │── SecureRandom → DEK (256bit)│
  │── AES-GCM(photo, DEK) → ct  │
  │── Sign(metadata, PrivKey)    │
  │                              │
  │── POST /api/photo/upload     │
  │   {sn, ct, iv, dek, sig}   ──►│
  │                              │── 验签PubKey
  │                              │── wrapDEK(dek, UK) → encDEK
  │                              │── 存储 {ct, iv, encDEK, sig}
  │◄── {photoId} ───────────────│

Phase 3恢复出厂 → 短信验证 → 恢复

设备端(新)                     后端                          用户端
  │                              │                              │
  │── 新 TEE 密钥对               │                              │
  │── POST /api/device/register  │                              │
  │   {sn, newPubKey}           ──►│(旧设备自动停用)            │
  │◄── {newDeviceId} ───────────│                              │
  │                              │                              │
  │                              │◄── POST /api/device/sms/send  │
  │                              │    {phone}                    │
  │                              │── 发送短信                     │
  │                              │                              │
  │                              │◄── POST /api/device/recover   │
  │                              │    {userId, sn, smsCode,     │
  │                              │     newPubKey}               │
  │                              │── ① 验证短信                  │── 输入验证码
  │                              │── ② 确认 SN 归属              │
  │                              │── ③ 生成 Recovery Token       │
  │                              │── ④ RSA(newPubKey, Token)     │
  │◄── {encToken, nonce} ────────│                              │
  │                              │                              │
  │── PrivKey 解密 Token          │                              │
  │── POST /api/photo/recover    │                              │
  │   {deviceId, token}         ──►│                              │
  │                              │── ① 验证 Token                │
  │                              │── ② 遍历照片                   │
  │                              │── ③ UK 解 DEK → PubKey 加密   │
  │◄── [{photoId, encDEK}, ...] ─│                              │
  │                              │                              │
  │── PrivKey 解密每个 DEK        │                              │
  │── AES-GCM 解密照片            │                              │
  │── ✅ 照片恢复完成             │                              │

📁 项目结构

secure-device-demo/
├── android-device/
│   └── DeviceCrypto.java          # Android 端完整安全模块
│
├── springboot-server/
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   ├── java/com/secure/demo/
│       │   │   ├── SecureDemoApplication.java
│       │   │   ├── controller/
│       │   │   │   └── DeviceController.java
│       │   │   ├── service/
│       │   │   │   ├── KeyManagementService.java
│       │   │   │   └── DeviceBindingService.java
│       │   │   ├── model/
│       │   │   │   ├── User.java
│       │   │   │   ├── Device.java
│       │   │   │   └── EncryptedPhoto.java
│       │   │   └── crypto/
│       │   │       ├── AesGcmUtil.java
│       │   │       └── RsaUtil.java
│       │   └── resources/
│       │       └── application.properties
│       └── test/
│           ├── java/com/secure/demo/
│           │   └── IntegrationTest.java
│           └── resources/
│               └── application-test.properties
│
└── README.md

🚀 快速启动

后端

cd springboot-server
mvn spring-boot:run

测试

cd springboot-server
mvn test

Android 端集成

DeviceCrypto.java 复制到 Android 项目的对应包路径下, 在 ApplicationMainActivity 中初始化:

DeviceCrypto crypto = new DeviceCrypto(context);

// 注册
String pubKey = crypto.getPublicKeyBase64();
String sn = crypto.getDeviceSN();
// → POST /api/device/register {sn, publicKeyBase64: pubKey}

// 拍照加密
byte[] photo = capturePhoto();
DeviceCrypto.EncryptedPayload payload = crypto.encryptData(photo);
String signature = crypto.signMetadata(sn + "|" + timestamp + "|" + photoId);
// → POST /api/photo/upload {sn, photoId, ciphertextBase64, ivBase64, 
//                            dekBase64: payload.dekBase64, 
//                            metadataSignature: signature, metadata}

🛡️ 安全分析

攻击场景 vs 防护

攻击场景 结果 原因
设备被 root 拿不到 TEE 私钥 Keystore 硬件保护
设备被盗 无法解密历史数据 无用户登录态
恢复出厂 旧密钥销毁 TEE 安全擦除
服务端被拖库 DEK 被 UK 加密 信封加密
SN 被伪造 无法绑定/恢复 需短信验证 + SN 归属校验
短信被截获 仍需 SN 归属 多层校验
旧设备残留 已停用 重新注册时停用旧设备

安全原则

  1. 设备零信任 — 设备只持有签名密钥,不持有解密密钥
  2. 前向安全 — 每次恢复生成新密钥对
  3. 信封加密 — DEK 永不明文存库
  4. 短信 + SN 双因子 — 恢复必须两者同时通过
  5. 一次性令牌 — Recovery Token 含 nonce + 时间窗口

⚠️ 生产环境注意事项

Demo 简化 生产环境应改为
UK 直接存 DB KMS 托管(阿里云 KMS / AWS KMS
短信码随机生成 对接腾讯云/阿里云短信服务
内存 ConcurrentHashMap JPA + PostgreSQL/MySQL
明文 DEK 传输 设备端用服务端公钥加密 DEK 后传输
无验签实现 服务端用设备公钥验证 ECDSA 签名
无频率限制 Redis 限流(短信/API
无审计日志 所有密钥操作写审计表
Recovery Token 无过期 加 5 分钟时间窗口 + Redis 防重放

📋 API 接口清单

Method Path 说明
POST /api/device/register 设备注册SN + 公钥)
POST /api/device/bind 用户绑定设备
POST /api/device/sms/send 发送短信验证码
POST /api/photo/upload 上传加密照片
POST /api/device/recover 恢复设备(短信验证)
POST /api/photo/recover 恢复后获取照片 DEK
GET /api/photo/{id}/decrypt 用户下载并解密照片
GET /api/health 健康检查
Description
No description provided
Readme 354 KiB
Languages
Java 95.1%
JavaScript 2.9%
HTML 1.3%
CSS 0.7%