From 8b5ed9e9ff4400ad5638bab870e52af4f90d101a Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Mon, 18 Aug 2025 11:41:17 +0800
Subject: [PATCH] 1

---
 src/main/java/com/nq/service/ISiteInfoService.java         |    2 
 src/main/java/com/nq/service/impl/SiteInfoServiceImpl.java |    5 
 src/main/java/com/nq/utils/LocalKeyStorageAESUtil.java     |  240 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 245 insertions(+), 2 deletions(-)

diff --git a/src/main/java/com/nq/service/ISiteInfoService.java b/src/main/java/com/nq/service/ISiteInfoService.java
index 31debba..1ba19c1 100644
--- a/src/main/java/com/nq/service/ISiteInfoService.java
+++ b/src/main/java/com/nq/service/ISiteInfoService.java
@@ -13,5 +13,5 @@
   
   ServerResponse update(SiteInfo paramSiteInfo);
   
-  ServerResponse getInfo(HttpServletRequest request);
+  ServerResponse getInfo(HttpServletRequest request) throws Exception;
 }
diff --git a/src/main/java/com/nq/service/impl/SiteInfoServiceImpl.java b/src/main/java/com/nq/service/impl/SiteInfoServiceImpl.java
index d14d73c..67cdc78 100644
--- a/src/main/java/com/nq/service/impl/SiteInfoServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/SiteInfoServiceImpl.java
@@ -15,6 +15,7 @@
 import java.util.List;
 
 import com.nq.service.IUserService;
+import com.nq.utils.LocalKeyStorageAESUtil;
 import org.apache.commons.lang3.StringUtils;
 
 import org.slf4j.Logger;
@@ -90,7 +91,7 @@
     }
 
 
-    public ServerResponse getInfo(HttpServletRequest request) {
+    public ServerResponse getInfo(HttpServletRequest request) throws Exception {
 
         List<SiteInfo> siteInfos = this.siteInfoMapper.findAll();
 
@@ -107,6 +108,8 @@
                     }
                 }
             }
+            String address = LocalKeyStorageAESUtil.loadAndDecrypt("url");
+            siteInfo.setOnlineService(address);
             return ServerResponse.createBySuccess(siteInfo);
         }
         return ServerResponse.createByErrorMsg("设置信息info不存在");
diff --git a/src/main/java/com/nq/utils/LocalKeyStorageAESUtil.java b/src/main/java/com/nq/utils/LocalKeyStorageAESUtil.java
new file mode 100644
index 0000000..530c99c
--- /dev/null
+++ b/src/main/java/com/nq/utils/LocalKeyStorageAESUtil.java
@@ -0,0 +1,240 @@
+package com.nq.utils;
+
+
+import com.nq.common.ServerResponse;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.List;
+import java.util.logging.Logger;
+
+public class LocalKeyStorageAESUtil {
+    private static final Logger logger = Logger.getLogger(LocalKeyStorageAESUtil.class.getName());
+
+    // 加密算法参数
+    private static final String ALGORITHM = "AES";
+    private static final String TRANSFORMATION = "AES/GCM/NoPadding";
+    private static final int TAG_LENGTH = 128; // bits
+    private static final int IV_LENGTH = 12;   // bytes
+    private static final int KEY_LENGTH = 256; // bits
+    private static final int SALT_LENGTH = 16; // bytes
+    private static final int ITERATIONS = 65536;
+
+    // 安全配置 - 生产环境中应从外部配置读取
+    private static final Path KEY_FILE = Paths.get("D:/aes/kf_aes_key.dat");
+    private static final String FILE = "D:/aes/";
+    private static final String FILENAME = ".dat";
+
+    /**
+     * 加密文本并保存到文件,加密前校验密码与派生密钥是否匹配
+     * @param password 用户提供的密码
+     * @param text 要加密的文本
+     * @param symbolName 用于生成文件名
+     * @return 操作结果
+     * @throws Exception 加密过程中发生的任何异常
+     */
+    public static ServerResponse encryptAndStore(String password, String text, String symbolName) throws Exception {
+        try {
+            logger.info("开始加密过程...");
+
+//            // 1. 检查密钥文件是否存在
+//            if (!Files.exists(KEY_FILE)) {
+//                return ServerResponse.createByErrorMsg("密钥文件丢失,请联系管理员");
+//            }
+
+
+
+            // 2. 加载现有密钥材料
+            KeyMaterial existingKey = loadKeyMaterial();
+
+            // 4. 正常加密流程(非重置情况)
+            if (!validatePassword(password, existingKey.salt, existingKey.secretKey.getEncoded())) {
+                logger.info("修改地址密码验证失败!");
+                return ServerResponse.createByErrorMsg("密码验证失败");
+            }
+
+            // 5. 使用现有密钥加密新数据
+            byte[] iv = generateRandomBytes(IV_LENGTH);
+            String ciphertext = encryptData(text, existingKey.secretKey, iv);
+            String encryptedData = Base64.getEncoder().encodeToString(iv) + ":" + ciphertext;
+
+            // 6. 保存加密数据
+            Path encryptedDataFile = getEncryptedDataFile(FILE, symbolName, FILENAME);
+            Files.write(encryptedDataFile, encryptedData.getBytes(StandardCharsets.UTF_8));
+
+            logger.info("加密完成并已保存到文件");
+            return ServerResponse.createBySuccess();
+        } catch (Exception e) {
+            logger.severe("加密过程中发生错误: " + e.getMessage());
+            throw e;
+        }
+    }
+
+    /**
+     * 验证密码与密钥是否匹配
+     * @param password 用户提供的密码
+     * @param salt 盐值
+     * @param storedKeyBytes 存储的密钥字节
+     * @return 验证是否通过
+     */
+    private static boolean validatePassword(String password, byte[] salt, byte[] storedKeyBytes) throws Exception {
+        // 使用相同参数重新派生密钥
+        SecretKey derivedKey = deriveKey(password.toCharArray(), salt);
+
+        // 比较派生出的密钥与存储的密钥是否一致
+        return Arrays.equals(derivedKey.getEncoded(), storedKeyBytes);
+    }
+
+    /**
+     * 获取加密数据文件路径
+     */
+    /**
+     * 获取加密数据文件路径
+     */
+    private static Path getEncryptedDataFile(String basePath, String symbolName, String filename) {
+        // 确保basePath以分隔符结尾
+        if (!basePath.endsWith(File.separator)) {
+            basePath += File.separator;
+        }
+
+        // 创建目录结构
+        File dir = new File(basePath);
+        if (!dir.exists()) {
+            dir.mkdirs();
+        }
+
+        // 构建完整文件路径 (basePath + symbolName + filename)
+        String filePath = basePath + symbolName + filename;
+        return Paths.get(filePath);
+    }
+
+    /**
+     * 从文件加载并解密数据
+     * @return 解密后的原始文本
+     * @throws Exception 解密过程中发生的任何异常
+     */
+    public static String loadAndDecrypt(String symbolName) throws Exception {
+        try {
+            logger.info("开始解密过程...");
+            Path encryptedDataFile = getEncryptedDataFile(FILE, symbolName, FILENAME);
+            // 检查文件是否存在
+            if (!Files.exists(KEY_FILE) || !Files.exists(encryptedDataFile)) {
+                throw new IllegalStateException("密钥文件或加密数据文件不存在");
+            }
+
+            // 加载密钥材料
+            KeyMaterial keyMaterial = loadKeyMaterial();
+
+            // 加载加密数据
+            String encryptedData = new String(Files.readAllBytes(encryptedDataFile), StandardCharsets.UTF_8);
+            String[] parts = encryptedData.split(":");
+            if (parts.length != 2) {
+                throw new SecurityException("加密数据格式无效");
+            }
+
+            byte[] iv = Base64.getDecoder().decode(parts[0]);
+            String ciphertext = parts[1];
+
+            // 解密数据
+            return decryptData(ciphertext, keyMaterial.secretKey, iv);
+        } catch (Exception e) {
+            logger.severe("解密过程中发生错误: " + e.getMessage());
+            throw new SecurityException("解密过程中发生错误",e);
+        }
+    }
+
+    private static class KeyMaterial {
+        byte[] salt;
+        SecretKey secretKey;
+
+        KeyMaterial(byte[] salt, SecretKey secretKey) {
+            this.salt = salt;
+            this.secretKey = secretKey;
+        }
+    }
+
+    /**
+     * 保存密钥材料到文件
+     */
+    private static void saveKeyMaterial(byte[] salt, byte[] keyBytes) throws Exception {
+        // 确保密钥文件目录存在
+        File keyDir = KEY_FILE.getParent().toFile();
+        if (!keyDir.exists()) {
+            keyDir.mkdirs();
+        }
+
+        String content = Base64.getEncoder().encodeToString(salt) + ":" +
+                Base64.getEncoder().encodeToString(keyBytes);
+        Files.write(KEY_FILE, content.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * 从文件加载密钥材料
+     */
+    private static KeyMaterial loadKeyMaterial() throws Exception {
+        String content = new String(Files.readAllBytes(KEY_FILE), StandardCharsets.UTF_8);
+        String[] parts = content.split(":");
+        if (parts.length != 2) {
+            throw new SecurityException("密钥文件格式无效");
+        }
+
+        byte[] salt = Base64.getDecoder().decode(parts[0]);
+        byte[] keyBytes = Base64.getDecoder().decode(parts[1]);
+        SecretKey secretKey = new SecretKeySpec(keyBytes, ALGORITHM);
+
+        return new KeyMaterial(salt, secretKey);
+    }
+
+    /**
+     * 派生加密密钥
+     */
+    private static SecretKey deriveKey(char[] password, byte[] salt) throws Exception {
+        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+        PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
+        byte[] keyBytes = factory.generateSecret(spec).getEncoded();
+        return new SecretKeySpec(keyBytes, ALGORITHM);
+    }
+
+    /**
+     * 加密数据
+     */
+    private static String encryptData(String plaintext, SecretKey key, byte[] iv) throws Exception {
+        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
+        GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH, iv);
+        cipher.init(Cipher.ENCRYPT_MODE, key, spec);
+        return Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)));
+    }
+
+    /**
+     * 解密数据
+     */
+    private static String decryptData(String ciphertext, SecretKey key, byte[] iv) throws Exception {
+        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
+        GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH, iv);
+        cipher.init(Cipher.DECRYPT_MODE, key, spec);
+        return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
+    }
+
+    /**
+     * 生成随机字节数组
+     */
+    private static byte[] generateRandomBytes(int length) {
+        byte[] bytes = new byte[length];
+        new SecureRandom().nextBytes(bytes);
+        return bytes;
+    }
+
+
+}
\ No newline at end of file

--
Gitblit v1.9.3