# 应用密码改造 — 技术方案

> 巴青牧业数字化平台国密合规改造总体技术方案。  
> 依据等保及行业密码应用要求：身份鉴别对接签名验签服务器；存储加解密对接密码机；完整性校验与篡改告警。

---

## 1. 技术架构

| 项 | 说明 |
| --- | --- |
| **整体** | RuoYi **v3.9.2**（springboot2）单体 + Vue2 管理端 + 移动端/App |
| **运行时** | JDK 8、Spring Boot 2.x、Spring Security、MyBatis、Redis |
| **数据库** | MySQL 5.7+，InnoDB，utf8mb4 |
| **密码设施** | 签名验签服务器（SM2 验签）、密码机 HSM（SM3-HMAC、SM4） |
| **短信** | 互联网管理端双因子（账号口令 + 短信验证码） |

### 1.1 逻辑架构

```mermaid
flowchart TB
    subgraph client [客户端]
        UKey[国密 UKey]
        Browser[管理端浏览器]
        App[移动端/App]
    end

    subgraph app [应用层 baqing-admin]
        LoginGW[登录网关]
        CertLogin[证书登录]
        SmsLogin[短信双因子登录]
        AuthFilter[Spring Security 过滤器链]
        CryptoSDK[CryptoService 密码服务封装]
        IntegritySvc[IntegrityService 完整性服务]
        MyBatisCrypto[MyBatis 加解密拦截器]
        AlertSvc[SecurityAlertService 告警]
    end

    subgraph external [外部设施]
        SignServer[签名验签服务器]
        HSM[密码机]
        SMS[短信网关]
    end

    subgraph storage [存储]
        DB[(MySQL)]
        Redis[(Redis)]
    end

    Browser --> LoginGW
    UKey --> SignServer
    CertLogin --> SignServer
    SmsLogin --> SMS
    LoginGW --> AuthFilter
    AuthFilter --> CryptoSDK
    MyBatisCrypto --> CryptoSDK
    IntegritySvc --> CryptoSDK
    CryptoSDK --> HSM
    IntegritySvc --> DB
    AlertSvc --> DB
    LoginGW --> Redis
```

### 1.2 设计原则

| 原则 | 说明 |
| --- | --- |
| 密钥不出机 | 主密钥仅存密码机，应用仅持有密钥索引 `keyIndex` |
| 统一封装 | 业务代码禁止直接调用厂商 SDK，统一经 `CryptoService` |
| 分类分级 | 按数据类型选择「仅完整性」或「机密性 + 完整性」 |
| 可审计 | 验签、加解密、校验失败均记安全日志，日志本身做 HMAC |
| 可演进 | 支持密钥版本 `key_ver` 轮换与存量数据重算 |

---

## 2. 身份鉴别改造

### 2.1 登录策略矩阵

| 场景 | 用户类型 | 最低要求 | 说明 |
| --- | --- | --- | --- |
| 内网/专网管理端 | 系统管理员 | **国密 UKey 证书** | 对接签名验签服务器 SM2 验签 |
| 互联网管理端 | 系统管理员 | **账号口令 + 短信验证码** | 双因子，缺一不可 |
| 业务操作员 | 按角色配置 | 口令 + 可选短信/UKey | 由 `sys_login_policy` 配置 |
| 移动端/App | 普通用户 | 现有口令/Token 体系 | 敏感接口可增加短信二次确认 |

### 2.2 管理员 UKey 证书登录流程（⏳ 待实施）

> 以下为设计方案；本期已撤销接口实现，仅保留 `sys_user.cert_sn` 等字段与 `LoginPolicyType.CERT` 枚举。

```mermaid
sequenceDiagram
    participant U as 管理员浏览器
    participant C as UKey控件/中间件
    participant API as baqing-admin
    participant SS as 签名验签服务器
    participant R as Redis

    U->>API: GET /login/cert/challenge
    API->>R: 存 challenge（TTL 5min）
    API-->>U: challenge
    U->>C: 读取证书 + SM2 签名(challenge)
    C-->>U: certBase64 + signedData
    U->>API: POST /login/cert {cert, signedData, challenge}
    API->>SS: 验签(cert, signedData, challenge)
    SS-->>API: 验签结果
    API->>API: 按 cert_sn 查 sys_user
    API-->>U: JWT Token
    API->>API: 写 sys_logininfor + 安全审计
```

**后端改造点（待实施）**

| 模块 | 改造内容 |
| --- | --- |
| `SysLoginController` | 新增 `GET /login/cert/challenge`、`POST /login/cert` |
| `sys_user` | 增加 `cert_sn`、`cert_subject`、`login_policy`（**字段已预留**） |
| `TokenService` | 证书登录成功后与普通登录共用 JWT 签发逻辑 |
| 登录服务 | 增加证书验签登录与验签服务器对接 |
| 权限 | 超级管理员角色可配置强制证书登录 |

**验签失败处理**

- 记录 `sys_logininfor`（失败原因：验签失败/证书过期/用户未绑定）
- 连续失败 N 次触发 IP 限流（复用现有登录失败策略）

### 2.3 互联网双因子登录（口令 + 短信）

**流程**

1. `POST /login/sms/send`：提交账号 → 发送短信 OTP 至 `sys_user.phonenumber`（不校验图形验证码）
2. `POST /login`：提交 `username`、`password`、`smsCode`（不要求图形验证码）
3. 校验短信 OTP（Redis）→ 校验口令（BCrypt）→ 签发 Token，写登录日志

**改造点（一期已实施）**

| 项 | 说明 |
| --- | --- |
| Redis Key | `login:sms:{mobile}` / `login:sms:limit:{ip}` |
| OTP 规则 | 6 位数字，5 分钟有效，校验成功后一次性删除 |
| `sys_user.phonenumber` | 一期读明文列；二期可改为 SM4 解密 |
| 双因子总开关 | `login.auth-mode: sms-2fa` 或 `login.internet-sms.enabled: true` |
| 若依默认登录 | `login.auth-mode: ruoyi`（**单配置项**恢复改造前行为） |
| 策略表 | `sys_login_policy` + `login.network-zone` |
| 短信网关 | `AliyunSmsGatewayClient`（`alibabacloud-dysmsapi20170525`） |
| 开发 Stub | `sms.aliyun.dry-run=true` 时仅写日志，关键字 `【短信Stub】` |

**后端类路径**

| 类 | 模块 |
| --- | --- |
| `LoginSmsService` | `ruoyi-framework` |
| `SysLoginService.login(..., smsCode)` | `ruoyi-framework` |
| `SysLoginController` | `baqing-admin` |
| `SysLoginPolicyServiceImpl` | `ruoyi-system` |
| `AliyunSmsGatewayClient` | `ruoyi-framework` |

详见 [配置与接口说明.md](./配置与接口说明.md)。

### 2.4 与现有 RuoYi 权限衔接

- 身份鉴别仅解决「你是谁」；授权仍走 `sys_role` / `sys_menu` / `@PreAuthorize`
- **权限相关表纳入完整性保护**（见第 4 节），防止越权篡改

---

## 3. 密码机对接 — 存储加密

### 3.1 模块划分（`ruoyi-crypto`，一期已建）

```
ruoyi-crypto/
├── config/
│   ├── CryptoProperties.java          # 密码机连接、密钥索引
│   └── CryptoAutoConfiguration.java   # Spring Boot 自动配置
├── enums/
│   └── CryptoDataType.java
├── service/
│   ├── CryptoService.java             # 统一对外接口
│   └── impl/StubCryptoServiceImpl.java  # 开发态 Stub（BouncyCastle）
└── exception/
    └── CryptoException.java
```

生产环境将 `StubCryptoServiceImpl` 替换为厂商 HSM SDK 实现，接口不变。

### 3.2 统一服务接口（概念）

```java
public interface CryptoService {
    /** SM3-HMAC，返回 Base64 */
    String hmacSm3(byte[] plain, int keyIndex, CryptoDataType dataType);

    /** SM4 加密，返回 Base64 密文 */
    String encryptSm4(String plainText, int keyIndex, CryptoDataType dataType);

    /** SM4 解密 */
    String decryptSm4(String cipherText, int keyIndex, CryptoDataType dataType);

    /** 完整性校验 */
    boolean verifyHmac(byte[] plain, String storedHmac, int keyIndex, CryptoDataType dataType);
}
```

- `keyIndex`：密码机内密钥槽位，按数据类型分钥（见《配置与接口说明.md》）
- 所有调用记 `sys_crypto_audit_log`（操作类型、dataType、结果，**不含明文**）

### 3.3 数据保护策略总表

| 数据类别 | SM3 HMAC 完整性 | SM4 机密性 | 说明 |
| --- | --- | --- | --- |
| 身份鉴别数据 | ✅ | ✅ | 账号口令、登录相关敏感配置 |
| 个人信息 | ✅ | ✅ | 姓名、手机、邮箱、证件号等 |
| 权限信息 | ✅ | — | 用户、角色、菜单关联（一般不需 SM4） |
| 操作日志 | ✅ | — | 操作内容摘要做 HMAC，防删改 |
| 主要业务数据 | ✅ | ✅ | 产业各业务表敏感列 |

详细字段见 [数据分类与字段加密映射表.md](./数据分类与字段加密映射表.md)。

### 3.4 存储模型

**机密性字段（SM4）**

```text
原列改为存密文，或新增 *_cipher 列：
  phonenumber_cipher   VARCHAR(256)   -- SM4 Base64 密文
  phonenumber_hmac     VARCHAR(128)   -- SM3 HMAC
  crypto_key_ver       INT            -- 密钥版本，轮换用
```

**仅完整性字段（SM3）**

```text
行级或逻辑组：
  integrity_hmac       VARCHAR(128)
  integrity_key_ver    INT
```

**Canonical 拼接规则（HMAC 输入，必须固定）**

```text
payload = dataType + "|" + keyVer + "|" + field1 + "|" + field2 + "|" ...
```

- 字段顺序、空值用固定占位符（如 `<NULL>`）写入规范文档，版本升级时兼容旧 `key_ver`

### 3.5 写入 / 读取拦截

| 方式 | 适用 | 说明 |
| --- | --- | --- |
| MyBatis 拦截器 | 推荐 | 按 `sys_crypto_field_config` 自动加解密、写 HMAC |
| Service AOP | 补充 | `@CryptoField`、`@IntegrityProtected` 注解 |
| 定时任务 | 存量迁移 | 批量重加密、重算 HMAC |

**写路径**

1. 业务 Service 提交明文实体  
2. 拦截器查配置 → SM4 加密敏感列 → SM3 计算 HMAC → 写库  

**读路径**

1. 拦截器读密文 → SM4 解密  
2. 重算 HMAC 与库中比对 → 不一致则告警并阻断（见第 5 节）

### 3.6 口令存储说明

| 方案 | 说明 |
| --- | --- |
| **测评要求 SM4 存口令** | `password` 列存 SM4 密文；登录时解密后比对或调密码机专用校验接口 |
| **测评允许单向摘要** | 保留 BCrypt，传输链路与备份用国密；存储层按测评口径二选一 |

**实施前必须与测评机构确认口径**，并在映射表中定稿。

---

## 4. 完整性校验与篡改告警

### 4.1 校验时机

| 时机 | 动作 |
| --- | --- |
| **读时校验** | 查询解密后重算 HMAC，与库中 `*_hmac` 比对 |
| **写前校验** | 更新前先校验旧记录完整性，失败则拒绝更新 |
| **登录/鉴权** | 加载用户、角色、菜单权限前校验关联数据完整性 |
| **定时巡检** | 夜间全表/抽样校验，发现静默篡改 |

### 4.2 校验流程

```text
读取记录
  → 若含密文字段：SM4 解密得明文
  → 按 Canonical 规则拼接待保护串
  → 调密码机 SM3-HMAC
  → 与 integrity_hmac / 字段 hmac 比较
  → 不一致 → 记安全事件 + 告警 + 业务阻断/降级
```

### 4.3 告警分级

| 级别 | 场景 | 系统动作 |
| --- | --- | --- |
| **高** | 用户口令、角色权限被篡改 | 阻断访问、锁定账号、通知安全管理员 |
| **中** | 个人信息、业务主数据篡改 | 拒绝写操作、页面提示、记安全事件 |
| **低** | 单条操作日志 HMAC 失败 | 记事件、不影响其他业务只读 |

### 4.4 用户提示文案（产品）

- 管理端：`数据完整性校验失败，疑似被非法篡改，请联系系统管理员。`
- 不向用户展示 HMAC、密钥索引等内部信息

### 4.5 告警通道

- 表 `sys_security_alert` 持久化
- 对接：站内消息（若依通知）、短信、运维监控平台
- 与 `sys_oper_log` 区分：后者为业务操作日志，前者为密码/完整性安全事件

---

## 5. 与现有工程的集成点

| 现有模块 | 集成方式 |
| --- | --- |
| `ruoyi-framework` / `TokenService` | 登录成功后触发权限数据完整性抽检（可选） |
| `ruoyi-system` / `SysUserService` | 用户增删改走加解密拦截器 |
| `LogAspect` / `sys_oper_log` | 写入后异步计算行 HMAC |
| `baqing-admin` 各 `biz_*` 业务 | 按映射表配置敏感字段 |
| `ruoyi-quartz` | 完整性巡检任务 `CryptoIntegrityJob` |

---

## 6. 非功能要求

| 项 | 要求 |
| --- | --- |
| 性能 | 密码机连接池；批量读可做短 TTL 校验结果缓存（须评估安全） |
| 高可用 | 密码机双机热备；SDK 支持 failover |
| 密钥轮换 | `key_ver` 递增；后台任务重算 HMAC / 重加密 |
| 审计 | 所有密码调用、验签、校验失败可追溯 |
| 灾备 | 密钥备份恢复流程与等保测评一致 |

---

## 7. 分期实施（概要）

详见 [实施计划与验收.md](./实施计划与验收.md)。

| 期次 | 内容 | 状态 |
| --- | --- | --- |
| 一期 | 短信双因子后端、`ruoyi-crypto` Stub、阿里云短信 SDK、`auth-mode` 切换、前端短信双因子 | ✅ |
| 一期余 | UKey 证书登录 | ⏳ 待实施（字段/策略枚举已预留） |
| 二期 | `sys_user` / 权限表 / 日志表 SM3；个人信息 SM4 | ⏳ |
| 三期 | 业务表按映射表改造；存量迁移 | ⏳ |
| 四期 | 定时巡检、告警中心、密钥轮换 | ⏳ |

---

## 8. 风险与注意事项

1. **禁止应用内硬编码密钥**；仅使用密码机密钥索引。  
2. **权限表篡改危害最大**，必须读时 + 写后 + 定时三重校验。  
3. **日志防删改**：HMAC + 考虑独立审计库或只追加存储。  
4. **Canonical 规则变更**须升 `key_ver`，避免误报。  
5. **改造前**先评审《数据分类与字段加密映射表》，再动表结构。  
6. **厂商 SDK** 线程安全与连接泄漏需在压测阶段验证。
