CheckUtil.java 4.9 KB
package com.aigeo.util.pay;


import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * 项目名称:	 微信支付回调解密
 * 版权所有:  深圳市科飞时速网络技术有限公司(0755-88843776)
 * 程序版本:  V3.0
 * 技术支持:  info@21gmail.com
 * 单元名称:  支付接口
 * 开始时间:  2025/08/04 15:13
 * 最后修改:  2025/08/04 15:13
 * 开发人员:  温志锋
 * 备   注:   如需修改请联系开发人员
 */
public class CheckUtil {
    /**
     * 验签
     * @param wechatPublicKeyPath 公钥文件路径
     * @param timestamp 时间戳
     * @param nonce 验签的随机字符串
     * @param body 请求体
     * @param signature 签名值
     * @return 验签成功
     */
    public static boolean verifySignature(String wechatPublicKeyPath, String timestamp, String nonce, String body, String signature){
        try{
            // 1. 构建验签串
            String signMessage = buildSignMessage(timestamp, nonce, body);
            // 2. 解码Base64签名
            byte[] signatureBytes = Base64.getDecoder().decode(signature);
            // 3. 加载微信支付公钥
            PublicKey publicKey = loadPublicKey(wechatPublicKeyPath);
            // 4. 执行验签
            Signature signer = Signature.getInstance("SHA256withRSA");

            signer.initVerify(publicKey);

            signer.update(signMessage.getBytes(StandardCharsets.UTF_8));

            return signer.verify(signatureBytes);
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("---------验签异常---------"+e);
            return false;
        }
    }

    /**
     * 解密方法
     * @param ciphertext 数据密文
     * @param associatedData 附加数据
     * @param nonce 随机串
     * @param vkey v3key
     * @return 解密数据
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     */
    public static String decryptData(String ciphertext, String associatedData, String nonce,String vkey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        byte[] keyBytes = vkey.getBytes(StandardCharsets.UTF_8);
        SecretKey key = new SecretKeySpec(keyBytes, "AES");

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
        cipher.init(Cipher.DECRYPT_MODE, key, spec);

        if (associatedData != null) {
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
        }

        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
        return new String(decrypted, StandardCharsets.UTF_8);
    }



    /**
     * 构建验签串
     * 格式: timestamp + "\n" + nonce + "\n" + body + "\n"
     */
    private static String buildSignMessage(String timestamp, String nonce, String body) {
        // 处理空body情况(如204响应)
        if (body == null) {
            body = "";
        }
        return timestamp + "\n" + nonce + "\n" + body + "\n";
    }

    /**
     * 从PEM格式字符串加载公钥
     */
    private static PublicKey loadPublicKey(String filePath) throws GeneralSecurityException {
        try {
            String pemContent = new String(Files.readAllBytes(Paths.get(filePath)), StandardCharsets.UTF_8);
             //1. 移除PEM头尾标记和空白字符
            String publicKeyPEM = pemContent
                    .replace("-----BEGIN PUBLIC KEY-----", "")
                    .replace("-----END PUBLIC KEY-----", "")
                    .replaceAll("\\s", "");
            System.out.println("----公钥:"+publicKeyPEM);

            // 2. Base64解码
            byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);

            // 3. 创建KeyFactory
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

            // 4. 生成公钥
            return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
            //byte[] certBytes = Files.readAllBytes(Paths.get(filePath));
            //CertificateFactory cf = CertificateFactory.getInstance("X.509");
            //X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certBytes));
            //return cert.getPublicKey();
        } catch (Exception e) {
            e.printStackTrace();
            throw new GeneralSecurityException("加载微信支付公钥失败", e);
        }
    }
}