|
|
|
@ -251,6 +251,14 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService |
|
|
|
return sysUserMapper.update(null, uw) > 0; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 解密获取明文(SM4-CBC 对称解密) |
|
|
|
* |
|
|
|
* @param tableName 业务表名 |
|
|
|
* @param bizId 业务记录主键 |
|
|
|
* @param fieldCode 字段编码 |
|
|
|
* @return 明文值,不存在则返回 null |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
public String decryptField(String tableName, String bizId, String fieldCode) { |
|
|
|
CryptoSensitiveData record = queryActiveRecord(tableName, bizId, fieldCode); |
|
|
|
@ -592,6 +600,103 @@ public class CryptoSensitiveHsmServiceImpl implements ICryptoSensitiveHsmService |
|
|
|
return stats; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public Map<String, Object> verifyUserDataConsistency(String loginName) { |
|
|
|
if (StrUtil.isBlank(loginName)) { |
|
|
|
throw new ServiceException("登录账号不能为空"); |
|
|
|
} |
|
|
|
SysUser user = DataPermissionHelper.ignore(() -> sysUserMapper.selectOne( |
|
|
|
new LambdaQueryWrapper<SysUser>() |
|
|
|
.eq(SysUser::getLoginName, loginName) |
|
|
|
.eq(SysUser::getDelFlag, "0") |
|
|
|
.last("limit 1"))); |
|
|
|
if (user == null) { |
|
|
|
throw new ServiceException("用户不存在: " + loginName); |
|
|
|
} |
|
|
|
Map<String, Object> result = new HashMap<>(); |
|
|
|
result.put("loginName", loginName); |
|
|
|
result.put("userId", user.getUserId()); |
|
|
|
boolean allConsistent = true; |
|
|
|
for (String fieldCode : new String[]{"password", "phonenumber", "certificateNumber"}) { |
|
|
|
Map<String, Object> item = verifyOneFieldConsistency(user, fieldCode); |
|
|
|
result.put(fieldCode, item); |
|
|
|
if (!Boolean.TRUE.equals(item.get("consistent"))) { |
|
|
|
allConsistent = false; |
|
|
|
} |
|
|
|
} |
|
|
|
result.put("consistent", allConsistent); |
|
|
|
log.info("指定账号敏感数据一致性校验完成, loginName={}, userId={}, 一致={}", |
|
|
|
loginName, user.getUserId(), allConsistent); |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 单字段一致性校验(三口径): |
|
|
|
* (1) 业务列值 == crypto_sensitive_data.cipher_text; |
|
|
|
* (2) 备份字段 crypto_remark_* == crypto_sensitive_data.field_value(原逻辑值); |
|
|
|
* (3) HSM 解密原逻辑值归一化到明文 == 备份字段解析明文(password 原逻辑值即 MD5, 与备份直接比对) |
|
|
|
*/ |
|
|
|
private Map<String, Object> verifyOneFieldConsistency(SysUser user, String fieldCode) { |
|
|
|
Map<String, Object> item = new HashMap<>(); |
|
|
|
String bizId = String.valueOf(user.getUserId()); |
|
|
|
String storedValue = switch (fieldCode) { |
|
|
|
case "password" -> user.getPassword(); |
|
|
|
case "phonenumber" -> user.getPhonenumber(); |
|
|
|
case "certificateNumber" -> user.getCertificateNumber(); |
|
|
|
default -> null; |
|
|
|
}; |
|
|
|
String remarkValue = getRemarkValue(user, fieldCode); |
|
|
|
CryptoSensitiveData record = queryActiveRecord(TABLE_SYS_USER, bizId, fieldCode); |
|
|
|
if (StrUtil.isBlank(storedValue) && record == null) { |
|
|
|
item.put("status", "skip"); |
|
|
|
item.put("consistent", true); |
|
|
|
item.put("message", "字段为空且无密评记录, 跳过"); |
|
|
|
return item; |
|
|
|
} |
|
|
|
if (record == null || StrUtil.isBlank(record.getCipherText())) { |
|
|
|
item.put("status", "missingRecord"); |
|
|
|
item.put("consistent", false); |
|
|
|
item.put("message", "crypto_sensitive_data 无活跃密文记录"); |
|
|
|
log.error("一致性校验: 密评记录缺失, userId={}, fieldCode={}", bizId, fieldCode); |
|
|
|
return item; |
|
|
|
} |
|
|
|
boolean cipherMatch = StrUtil.isNotBlank(storedValue) && storedValue.equals(record.getCipherText()); |
|
|
|
item.put("cipherMatch", cipherMatch); |
|
|
|
boolean backupMatch = StrUtil.equals(remarkValue, record.getFieldValue()); |
|
|
|
item.put("backupMatch", backupMatch); |
|
|
|
boolean plainMatch; |
|
|
|
try { |
|
|
|
String originalForm = decryptField(TABLE_SYS_USER, bizId, fieldCode); |
|
|
|
if (StrUtil.isBlank(originalForm)) { |
|
|
|
plainMatch = false; |
|
|
|
item.put("message", "HSM 解密失败"); |
|
|
|
} else if ("password".equals(fieldCode)) { |
|
|
|
plainMatch = originalForm.equals(remarkValue); |
|
|
|
} else { |
|
|
|
String hsmPlain = normalizeToPlain(fieldCode, originalForm); |
|
|
|
String backupPlain = StrUtil.isBlank(remarkValue) ? null : normalizeToPlain(fieldCode, remarkValue); |
|
|
|
plainMatch = StrUtil.isNotBlank(hsmPlain) && hsmPlain.equals(backupPlain); |
|
|
|
} |
|
|
|
} catch (Exception e) { |
|
|
|
// 按规范: HSM 调用失败记录日志不中断流程, 标记校验异常(非篡改)
|
|
|
|
log.error("一致性校验 HSM 解密异常, userId={}, fieldCode={}", bizId, fieldCode, e); |
|
|
|
plainMatch = false; |
|
|
|
item.put("status", "hsmError"); |
|
|
|
item.put("message", "HSM 解密异常: " + e.getMessage()); |
|
|
|
} |
|
|
|
item.put("plainMatch", plainMatch); |
|
|
|
boolean consistent = cipherMatch && backupMatch && plainMatch; |
|
|
|
item.put("consistent", consistent); |
|
|
|
if (!consistent) { |
|
|
|
item.put("status", StrUtil.isBlank((String) item.get("status")) ? "inconsistent" : item.get("status")); |
|
|
|
log.error("敏感数据一致性校验未通过, userId={}, cipherMatch={}, backupMatch={}, plainMatch={}", |
|
|
|
bizId, cipherMatch, backupMatch, plainMatch); |
|
|
|
} else { |
|
|
|
item.put("status", "ok"); |
|
|
|
} |
|
|
|
return item; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 查询指定业务记录是否已存在完整性摘要 |
|
|
|
*/ |
|
|
|
|