|
|
@@ -0,0 +1,323 @@
|
|
|
+package com.fs.company.util;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.Base64;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 加密解密工具类
|
|
|
+ * 用于接口入参解析、返回结果加解密处理
|
|
|
+ *
|
|
|
+ */
|
|
|
+public class CryptoUtil {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(CryptoUtil.class);
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ JSONObject jsonObject = new JSONObject();
|
|
|
+ jsonObject.put("name", "MixLiu");
|
|
|
+ jsonObject.put("age", 18);
|
|
|
+ jsonObject.put("sex", "男");
|
|
|
+ jsonObject.put("occupation","engineer");
|
|
|
+ String encrypt = encrypt(jsonObject.toJSONString());
|
|
|
+ System.out.println(encrypt);
|
|
|
+ String decrypt = decrypt(encrypt);
|
|
|
+ System.out.println(decrypt);
|
|
|
+ System.out.println(JSONObject.parse(decrypt));
|
|
|
+ }
|
|
|
+ // ==================== 秘钥配置 ====================
|
|
|
+ /**
|
|
|
+ * AES加密秘钥
|
|
|
+ * 【重要】请根据实际业务需求修改此秘钥
|
|
|
+ * 秘钥长度必须为16位(AES-128)、24位(AES-192)或32位(AES-256)
|
|
|
+ * 当前使用AES-128,秘钥长度为16位
|
|
|
+ */
|
|
|
+ private static final String AES_SECRET_KEY = "FsCmp@nyK3y!2026";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 备用秘钥(用于秘钥轮换场景)
|
|
|
+ * 【重要】生产环境请修改为不同的秘钥
|
|
|
+ */
|
|
|
+ private static final String AES_BACKUP_KEY = "BkFC0mp@nyK3y!26";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AES加密算法/模式/填充方式
|
|
|
+ * AES/ECB/PKCS5Padding:AES加密,ECB模式,PKCS5填充
|
|
|
+ */
|
|
|
+ private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AES算法名称
|
|
|
+ */
|
|
|
+ private static final String AES_KEY_ALGORITHM = "AES";
|
|
|
+
|
|
|
+ // ==================== 加密方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AES加密
|
|
|
+ * 用于加密入参或返回结果
|
|
|
+ *
|
|
|
+ * @param plainText 明文
|
|
|
+ * @return Base64编码的密文,加密失败返回null
|
|
|
+ */
|
|
|
+ public static String encrypt(String plainText) {
|
|
|
+ return encrypt(plainText, AES_SECRET_KEY);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AES加密(使用指定秘钥)
|
|
|
+ *
|
|
|
+ * @param plainText 明文
|
|
|
+ * @param secretKey 秘钥
|
|
|
+ * @return Base64编码的密文,加密失败返回null
|
|
|
+ */
|
|
|
+ public static String encrypt(String plainText, String secretKey) {
|
|
|
+ if (plainText == null || secretKey == null) {
|
|
|
+ log.warn("加密参数不能为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), AES_KEY_ALGORITHM);
|
|
|
+ Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec);
|
|
|
+ byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
|
|
+ return Base64.getEncoder().encodeToString(encryptedBytes);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("AES加密失败: {}", e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加密入参
|
|
|
+ * 将原始入参字符串加密后返回
|
|
|
+ *
|
|
|
+ * @param inputParam 原始入参
|
|
|
+ * @return 加密后的入参字符串
|
|
|
+ */
|
|
|
+ public static String encryptInput(String inputParam) {
|
|
|
+ log.debug("加密入参");
|
|
|
+ return encrypt(inputParam);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加密返回结果
|
|
|
+ * 将返回结果对象转换为JSON字符串后加密
|
|
|
+ *
|
|
|
+ * @param result 返回结果对象
|
|
|
+ * @return 加密后的结果字符串
|
|
|
+ */
|
|
|
+ public static String encryptResult(Object result) {
|
|
|
+ if (result == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String jsonStr;
|
|
|
+ if (result instanceof String) {
|
|
|
+ jsonStr = (String) result;
|
|
|
+ } else {
|
|
|
+ jsonStr = JSON.toJSONString(result);
|
|
|
+ }
|
|
|
+ log.debug("加密返回结果");
|
|
|
+ return encrypt(jsonStr);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 解密方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AES解密
|
|
|
+ * 用于解密入参或返回结果
|
|
|
+ *
|
|
|
+ * @param encryptedText Base64编码的密文
|
|
|
+ * @return 解密后的明文,解密失败返回null
|
|
|
+ */
|
|
|
+ public static String decrypt(String encryptedText) {
|
|
|
+ return decrypt(encryptedText, AES_SECRET_KEY);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AES解密(使用指定秘钥)
|
|
|
+ *
|
|
|
+ * @param encryptedText Base64编码的密文
|
|
|
+ * @param secretKey 秘钥
|
|
|
+ * @return 解密后的明文,解密失败返回null
|
|
|
+ */
|
|
|
+ public static String decrypt(String encryptedText, String secretKey) {
|
|
|
+ if (encryptedText == null || secretKey == null) {
|
|
|
+ log.warn("解密参数不能为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), AES_KEY_ALGORITHM);
|
|
|
+ Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, keySpec);
|
|
|
+ byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
|
|
|
+ return new String(decryptedBytes, StandardCharsets.UTF_8);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("AES解密失败: {}", e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解密入参
|
|
|
+ * 将加密的入参字符串解密
|
|
|
+ *
|
|
|
+ * @param encryptedInput 加密的入参
|
|
|
+ * @return 解密后的入参字符串
|
|
|
+ */
|
|
|
+ public static String decryptInput(String encryptedInput) {
|
|
|
+ log.debug("解密入参");
|
|
|
+ return decrypt(encryptedInput);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解密返回结果
|
|
|
+ * 将加密的返回结果解密
|
|
|
+ *
|
|
|
+ * @param encryptedResult 加密的返回结果
|
|
|
+ * @return 解密后的结果字符串
|
|
|
+ */
|
|
|
+ public static String decryptResult(String encryptedResult) {
|
|
|
+ log.debug("解密返回结果");
|
|
|
+ return decrypt(encryptedResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 解析方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析入参(解密后解析为JSON对象)
|
|
|
+ * 先解密入参,再将解密后的字符串解析为JSONObject
|
|
|
+ *
|
|
|
+ * @param encryptedInput 加密的入参
|
|
|
+ * @return 解析后的JSONObject,解析失败返回null
|
|
|
+ */
|
|
|
+ public static JSONObject parseInput(String encryptedInput) {
|
|
|
+ String decryptedStr = decryptInput(encryptedInput);
|
|
|
+ if (decryptedStr == null) {
|
|
|
+ log.warn("入参解密失败,无法解析");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return JSON.parseObject(decryptedStr);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("入参JSON解析失败: {}", e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析入参(解密后解析为指定类型对象)
|
|
|
+ * 先解密入参,再将解密后的字符串解析为指定类型的对象
|
|
|
+ *
|
|
|
+ * @param encryptedInput 加密的入参
|
|
|
+ * @param clazz 目标类型
|
|
|
+ * @param <T> 泛型类型
|
|
|
+ * @return 解析后的对象,解析失败返回null
|
|
|
+ */
|
|
|
+ public static <T> T parseInput(String encryptedInput, Class<T> clazz) {
|
|
|
+ String decryptedStr = decryptInput(encryptedInput);
|
|
|
+ if (decryptedStr == null) {
|
|
|
+ log.warn("入参解密失败,无法解析");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return JSON.parseObject(decryptedStr, clazz);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("入参解析为{}失败: {}", clazz.getSimpleName(), e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析返回结果(解密后解析为JSON对象)
|
|
|
+ * 先解密返回结果,再将解密后的字符串解析为JSONObject
|
|
|
+ *
|
|
|
+ * @param encryptedResult 加密的返回结果
|
|
|
+ * @return 解析后的JSONObject,解析失败返回null
|
|
|
+ */
|
|
|
+ public static JSONObject parseResult(String encryptedResult) {
|
|
|
+ String decryptedStr = decryptResult(encryptedResult);
|
|
|
+ if (decryptedStr == null) {
|
|
|
+ log.warn("返回结果解密失败,无法解析");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return JSON.parseObject(decryptedStr);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("返回结果JSON解析失败: {}", e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析返回结果(解密后解析为指定类型对象)
|
|
|
+ * 先解密返回结果,再将解密后的字符串解析为指定类型的对象
|
|
|
+ *
|
|
|
+ * @param encryptedResult 加密的返回结果
|
|
|
+ * @param clazz 目标类型
|
|
|
+ * @param <T> 泛型类型
|
|
|
+ * @return 解析后的对象,解析失败返回null
|
|
|
+ */
|
|
|
+ public static <T> T parseResult(String encryptedResult, Class<T> clazz) {
|
|
|
+ String decryptedStr = decryptResult(encryptedResult);
|
|
|
+ if (decryptedStr == null) {
|
|
|
+ log.warn("返回结果解密失败,无法解析");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return JSON.parseObject(decryptedStr, clazz);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("返回结果解析为{}失败: {}", clazz.getSimpleName(), e.getMessage(), e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 秘钥轮换相关 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用备用秘钥解密(用于秘钥轮换场景)
|
|
|
+ * 先尝试主秘钥解密,失败后尝试备用秘钥
|
|
|
+ *
|
|
|
+ * @param encryptedText 加密文本
|
|
|
+ * @return 解密后的明文
|
|
|
+ */
|
|
|
+ public static String decryptWithFallback(String encryptedText) {
|
|
|
+ String result = decrypt(encryptedText, AES_SECRET_KEY);
|
|
|
+ if (result == null) {
|
|
|
+ log.info("主秘钥解密失败,尝试使用备用秘钥");
|
|
|
+ result = decrypt(encryptedText, AES_BACKUP_KEY);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 工具方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证秘钥长度是否有效
|
|
|
+ *
|
|
|
+ * @param key 秘钥
|
|
|
+ * @return 是否有效
|
|
|
+ */
|
|
|
+ public static boolean isValidKeyLength(String key) {
|
|
|
+ if (key == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int len = key.getBytes(StandardCharsets.UTF_8).length;
|
|
|
+ return len == 16 || len == 24 || len == 32;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前使用的秘钥长度
|
|
|
+ *
|
|
|
+ * @return 秘钥长度
|
|
|
+ */
|
|
|
+ public static int getKeyLength() {
|
|
|
+ return AES_SECRET_KEY.getBytes(StandardCharsets.UTF_8).length;
|
|
|
+ }
|
|
|
+}
|