WangHan
2024-09-12 d5855a4926926698b740bc6c7ba489de47adb68b
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
package tech.powerjob.server.common.utils;
 
import lombok.SneakyThrows;
import tech.powerjob.common.utils.DigestUtils;
 
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
 
public class AESUtil {
 
 
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/GCM/NoPadding";
    private static final int KEY_SIZE = 256; // AES 256-bit
    private static final int GCM_NONCE_LENGTH = 12; // GCM nonce length (12 bytes)
    private static final int GCM_TAG_LENGTH = 16;  // GCM authentication tag length (16 bytes)
 
    // SecureRandom 实例,用于生成 nonce
    private static final SecureRandom secureRandom = new SecureRandom();
 
    /**
     * 生成密钥
     *
     * @param key 传入的密钥字符串,必须是 32 字节(256 位)长度
     * @return SecretKeySpec 实例
     */
    private static SecretKeySpec getKey(String key) {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        // 不足 32 字节,则使用 MD5 转为 32 位
        if (keyBytes.length != KEY_SIZE / 8) {
            keyBytes = DigestUtils.md5(key).getBytes(StandardCharsets.UTF_8);
        }
        return new SecretKeySpec(keyBytes, ALGORITHM);
    }
 
    /**
     * 加密
     *
     * @param data 要加密的数据
     * @param key  加密密钥
     * @return 加密后的数据(Base64 编码),包含 nonce
     */
    @SneakyThrows
    public static String encrypt(String data, String key) {
        byte[] nonce = new byte[GCM_NONCE_LENGTH];
        secureRandom.nextBytes(nonce); // 生成随机的 nonce
 
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
        cipher.init(Cipher.ENCRYPT_MODE, getKey(key), gcmParameterSpec);
 
        byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
 
        // 将 nonce 和密文连接在一起,然后进行 Base64 编码
        byte[] combinedData = new byte[nonce.length + encryptedData.length];
        System.arraycopy(nonce, 0, combinedData, 0, nonce.length);
        System.arraycopy(encryptedData, 0, combinedData, nonce.length, encryptedData.length);
 
        return Base64.getEncoder().encodeToString(combinedData);
    }
 
    /**
     * 解密
     *
     * @param encryptedData 要解密的数据(Base64 编码),包含 nonce
     * @param key           解密密钥
     * @return 解密后的数据
     */
    @SneakyThrows
    public static String decrypt(String encryptedData, String key) {
        byte[] combinedData = Base64.getDecoder().decode(encryptedData);
 
        // 提取 nonce
        byte[] nonce = new byte[GCM_NONCE_LENGTH];
        System.arraycopy(combinedData, 0, nonce, 0, nonce.length);
 
        // 提取实际的加密数据
        byte[] encryptedText = new byte[combinedData.length - nonce.length];
        System.arraycopy(combinedData, nonce.length, encryptedText, 0, encryptedText.length);
 
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
        cipher.init(Cipher.DECRYPT_MODE, getKey(key), gcmParameterSpec);
 
        byte[] decryptedData = cipher.doFinal(encryptedText);
        return new String(decryptedData, StandardCharsets.UTF_8);
    }
}