From f80d75969d2fe75aafa8ec1c798031efa1337deb Mon Sep 17 00:00:00 2001 From: zhangwenzan Date: Wed, 17 Sep 2025 14:17:20 +0800 Subject: [PATCH] fix --- .../core/security/EncryptionService.java | 55 ++++++++++++++++ .../converter/SensitiveDataConverter.java | 2 +- .../core/security/util/EsDataEncryptUtil.java | 65 +++++++++++-------- .../com/kakarote/crm/common/ElasticUtil.java | 15 ++++- .../crm/entity/DTO/CrmQueryGsdjxxDTO.java | 2 + 5 files changed, 110 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/com/kakarote/core/security/EncryptionService.java b/core/src/main/java/com/kakarote/core/security/EncryptionService.java index 1b2ba04..aaa6bff 100644 --- a/core/src/main/java/com/kakarote/core/security/EncryptionService.java +++ b/core/src/main/java/com/kakarote/core/security/EncryptionService.java @@ -13,6 +13,7 @@ import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.Security; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Base64; @Slf4j @@ -102,4 +103,58 @@ public class EncryptionService { throw new SecurityException("数据解密失败", e); } } + public String deterministicDecryptAes(String ciphertext) { + try { + // 使用与加密相同的固定IV + byte[] fixedIv = new byte[GCM_IV_LENGTH]; + Arrays.fill(fixedIv, (byte) 0x01); + + // 初始化密钥 + SecretKey secretKey = new SecretKeySpec(Base64.getDecoder().decode(systemKey), AES_KEY_ALGORITHM); + + // 初始化解密器 + Cipher cipher = Cipher.getInstance(AES_ALGORITHM); + GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, fixedIv); + cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec); + + // 解码Base64密文(注意:这里不包含IV) + byte[] decoded = Base64.getDecoder().decode(ciphertext); + + // 执行解密 + byte[] plaintext = cipher.doFinal(decoded); + return new String(plaintext, StandardCharsets.UTF_8); + } catch (Exception e) { + log.error("确定性AES解密失败", e); + throw new SecurityException("数据解密失败", e); + } + } + /** + * 确定性AES加密(用于查询场景) + * 使用固定IV,确保相同明文总是生成相同密文 + */ +public String deterministicEncryptAes(String plaintext) { + try { + // 使用固定IV(注意:在生产环境中,应考虑使用派生IV而不是固定IV) + byte[] fixedIv = new byte[GCM_IV_LENGTH]; + // 可以使用字段名或其他确定性数据来派生IV + Arrays.fill(fixedIv, (byte) 0x01); + + // 初始化密钥 + SecretKey secretKey = new SecretKeySpec(Base64.getDecoder().decode(systemKey), AES_KEY_ALGORITHM); + + // 初始化加密器 + Cipher cipher = Cipher.getInstance(AES_ALGORITHM); + GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, fixedIv); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); + + // 执行加密 + byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + + // 注意:这里不包含IV,因为IV是固定的 + return Base64.getEncoder().encodeToString(ciphertext); + } catch (Exception e) { + log.error("确定性AES加密失败", e); + throw new SecurityException("数据加密失败", e); + } +} } \ No newline at end of file diff --git a/core/src/main/java/com/kakarote/core/security/converter/SensitiveDataConverter.java b/core/src/main/java/com/kakarote/core/security/converter/SensitiveDataConverter.java index 9382b18..61b7b56 100644 --- a/core/src/main/java/com/kakarote/core/security/converter/SensitiveDataConverter.java +++ b/core/src/main/java/com/kakarote/core/security/converter/SensitiveDataConverter.java @@ -49,7 +49,7 @@ public class SensitiveDataConverter extends AbstractJsonTypeHandler impl public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException { if (s != null && !s.isEmpty() && !s.startsWith(Const.ENCRYPTED_PREFIX)) { // 加密后添加前缀标识 - s = Const.ENCRYPTED_PREFIX + getEncryptionService().encryptAes(s); + s = Const.ENCRYPTED_PREFIX + getEncryptionService().deterministicEncryptAes(s); } preparedStatement.setString(i, s); } diff --git a/core/src/main/java/com/kakarote/core/security/util/EsDataEncryptUtil.java b/core/src/main/java/com/kakarote/core/security/util/EsDataEncryptUtil.java index 8943d98..0ba5845 100644 --- a/core/src/main/java/com/kakarote/core/security/util/EsDataEncryptUtil.java +++ b/core/src/main/java/com/kakarote/core/security/util/EsDataEncryptUtil.java @@ -6,6 +6,7 @@ import com.kakarote.core.common.Const; import com.kakarote.core.common.FieldEnum; import com.kakarote.core.security.EncryptionService; import com.kakarote.core.servlet.ApplicationContextHolder; +import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import java.util.*; @@ -13,6 +14,7 @@ import java.util.*; /** * Elasticsearch数据加密解密工具类 */ +@Slf4j public class EsDataEncryptUtil { private static EncryptionService encryptionService; @@ -110,7 +112,7 @@ public class EsDataEncryptUtil { String strValue = (String) value; // 已经加密的不再重复加密 if (!strValue.isEmpty() && !strValue.startsWith(Const.ENCRYPTED_PREFIX)) { - return Const.ENCRYPTED_PREFIX + getEncryptionService().encryptAes(strValue); + return Const.ENCRYPTED_PREFIX + getEncryptionService().deterministicEncryptAes(strValue); } return strValue; } else if (value instanceof Map) { @@ -132,34 +134,43 @@ public class EsDataEncryptUtil { * 根据值类型进行解密 */ @SuppressWarnings("unchecked") - private static Object decryptValue(String key,Object value) { - if (value == null) { - return null; - } - - if (value instanceof String) { - String strValue = (String) value; - // 对已加密的数据进行解密 - if (strValue.startsWith(Const.ENCRYPTED_PREFIX)) { - String newValue = getEncryptionService().decryptAes(strValue.substring(Const.ENCRYPTED_PREFIX.length())); - return desensitization(key,newValue); - } - return strValue; - } else if (value instanceof Map) { - // 递归处理嵌套Map - return decryptMap((Map) value); - } else if (value instanceof List) { - // 递归处理List中的每个元素 - List decryptedList = new ArrayList<>(); - for (Object item : (List) value) { - decryptedList.add(decryptValue(key,item)); - } - return decryptedList; - } - // 其他类型保持不变 - return value; +private static Object decryptValue(String key, Object value) { + if (value == null) { + return null; } + if (value instanceof String) { + String strValue = (String) value; + // 对已加密的数据进行解密 + if (strValue.startsWith(Const.ENCRYPTED_PREFIX)) { + String encryptedValue = strValue.substring(Const.ENCRYPTED_PREFIX.length()); + // 尝试使用确定性解密(用于查询和存储中使用了deterministicEncryptAes的情况) + try { + String newValue = getEncryptionService().deterministicDecryptAes(encryptedValue); + return desensitization(key, newValue); + } catch (Exception e) { + // 如果确定性解密失败,尝试使用普通解密 + log.debug("确定性解密失败,尝试普通解密", e); + String newValue = getEncryptionService().decryptAes(encryptedValue); + return desensitization(key, newValue); + } + } + return strValue; + } else if (value instanceof Map) { + // 递归处理嵌套Map + return decryptMap((Map) value); + } else if (value instanceof List) { + // 递归处理List中的每个元素 + List decryptedList = new ArrayList<>(); + for (Object item : (List) value) { + decryptedList.add(decryptValue(key, item)); + } + return decryptedList; + } + // 其他类型保持不变 + return value; +} + // 数据脱敏 private static String desensitization(String key,String value){ if ("mobile".equals(key)) { diff --git a/crm/src/main/java/com/kakarote/crm/common/ElasticUtil.java b/crm/src/main/java/com/kakarote/crm/common/ElasticUtil.java index fc193af..8ab4403 100644 --- a/crm/src/main/java/com/kakarote/crm/common/ElasticUtil.java +++ b/crm/src/main/java/com/kakarote/crm/common/ElasticUtil.java @@ -255,7 +255,7 @@ public class ElasticUtil { if (value instanceof String) { String strValue = (String) value; if (!strValue.isEmpty() && !strValue.startsWith(Const.ENCRYPTED_PREFIX)) { - map.put(fieldName, Const.ENCRYPTED_PREFIX + ApplicationContextHolder.getBean(EncryptionService.class).encryptAes(strValue)); + map.put(fieldName, Const.ENCRYPTED_PREFIX + ApplicationContextHolder.getBean(EncryptionService.class).deterministicEncryptAes(strValue)); } else { map.put(fieldName, value); } @@ -303,10 +303,23 @@ public class ElasticUtil { if (search.getValues().size() == 0 && !Arrays.asList(5, 6).contains(search.getSearchEnum().getType())) { return; } + + // 对需要加密的字段值进行加密处理 + List encryptedValues = new ArrayList<>(); + for (String value : search.getValues()) { + if (!value.isEmpty() && !value.startsWith(Const.ENCRYPTED_PREFIX)) { + // 添加加密前缀并进行AES加密 + encryptedValues.add(Const.ENCRYPTED_PREFIX + ApplicationContextHolder.getBean(EncryptionService.class).deterministicEncryptAes(value)); + } else { + encryptedValues.add(value); + } + } + search.setValues(encryptedValues); switch (search.getSearchEnum()) { case IS: queryBuilder.filter(QueryBuilders.termsQuery(search.getName(), search.getValues())); break; + // 其他搜索条件处理保持不变 case IS_NOT: queryBuilder.mustNot(QueryBuilders.termsQuery(search.getName(), search.getValues())); break; diff --git a/crm/src/main/java/com/kakarote/crm/entity/DTO/CrmQueryGsdjxxDTO.java b/crm/src/main/java/com/kakarote/crm/entity/DTO/CrmQueryGsdjxxDTO.java index dac02c0..d382d75 100644 --- a/crm/src/main/java/com/kakarote/crm/entity/DTO/CrmQueryGsdjxxDTO.java +++ b/crm/src/main/java/com/kakarote/crm/entity/DTO/CrmQueryGsdjxxDTO.java @@ -16,6 +16,8 @@ public class CrmQueryGsdjxxDTO implements Serializable { // 登记序号 private String yf; + private String rq; + // 评价年度 private String xzqhDm;