1
zj
2025-08-18 8b5ed9e9ff4400ad5638bab870e52af4f90d101a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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;
    }
 
 
}