无关风月
2025-05-08 9486766c806fe1d9e082b2fd02ea1cc558f1b443
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
package com.dsh.activity.util.wx;
 
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
 
/**
 * 微信支付V3 AES解密工具类 (AEAD_AES_256_GCM)
 */
public class WxPayAesUtil {
 
    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int TAG_LENGTH_BIT = 128; // GCM认证标签长度,固定为128位
    private static final int NONCE_LENGTH_BYTE = 12; // GCM随机串长度,固定为12字节
    private static final String TRANSFORMATION = "AES/GCM/NoPadding"; // 算法/模式/填充
 
    /**
     * 解密微信支付回调通知中的加密信息
     *
     * @param apiV3Key      APIv3密钥 (来自 WxV3PayConfig)
     * @param associatedData 附加数据 (resource.associated_data)
     * @param nonce         随机串 (resource.nonce)
     * @param ciphertext    密文 (resource.ciphertext),Base64编码
     * @return 解密后的明文字符串
     * @throws GeneralSecurityException 解密失败时抛出异常
     */
    public static String decrypt(String apiV3Key, String associatedData, String nonce, String ciphertext)
            throws GeneralSecurityException {
        try {
            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce.getBytes(StandardCharsets.UTF_8));
 
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8)); // 设置附加认证数据
 
            byte[] decodedCiphertext = Base64.getDecoder().decode(ciphertext);
            byte[] decryptedBytes = cipher.doFinal(decodedCiphertext);
 
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException("当前JDK环境不支持AEAD_AES_256_GCM", e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException("无效的密钥或GCM参数", e);
        } catch (Exception e) {
            // 包括 BadPaddingException 等解密失败的情况
            throw new GeneralSecurityException("AES/GCM 解密失败", e);
        }
    }
}