# 应用密码改造 — 技术方案 > 巴青牧业数字化平台国密合规改造总体技术方案。 > 依据等保及行业密码应用要求:身份鉴别对接签名验签服务器;存储加解密对接密码机;完整性校验与篡改告警。 --- ## 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 证书登录流程 ```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 签发逻辑 | | `SysLoginService` | 增加 `loginByCert`、`verifyCertSignature` | | 权限 | 超级管理员角色可配置强制证书登录 | **验签失败处理** - 记录 `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 + "|" ... ``` - 字段顺序、空值用固定占位符(如 ``)写入规范文档,版本升级时兼容旧 `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** 线程安全与连接泄漏需在压测阶段验证。