微信支付使用Java解密时,抛出异常AEADBadTagException: Tag mismatch!

我们看一下官方文档说明
首先先自查三种原因

通常有三种可能:

  1. 使用了错误的API v3密钥,如使用了其他商户号的密钥,或者使用了APIv2的APIKey。
  2. 密文不正确。请检查提交解密的密文和收到的密文。注意报文中的密文经过了Base64编码。
  3. 解密时接口遗漏传入附加数据(associated_data)

如果以上都没有问题的话那么进入下一步操作

由于微信调用回调地址的时候可能并不是一次传输数据,如果使用@requestBody 标签去接收参数的话可能会不全,所以我们要使用 HttpServletRequest 去接收参数流,话不多说上代码

Controller层

@Autowired
    private WechatService wechatService;
@PostMapping("/wx/callback")
    public String wxCallback(HttpServletRequest request){
        return wechatService.callBack(request);
    }

业务代码层

 /**
     * 微信支付回调
     *
     * @param request 请求
     * @return 结果
     */
    @Override
    public String callBack(HttpServletRequest request) {
        // TODO 必须使用request获取body(readLine读取),微信推送的消息使用@RequestBody可能一次性无法读完,造成解密失败
        String resCode = "SUCCESS";
        String resMessage = "成功";
        String streamReadString = getRequestBody(request);
        WxPayCallbackModel model = JSONObject.parseObject(streamReadString, WxPayCallbackModel.class);
        log.info("pay callback: request read={}, request body read={}", streamReadString, model.toString());
        try {
            WxPayCallbackResourceModel resource = model.getResource();
            String associatedData = resource.getAssociated_data();
            String nonce = resource.getNonce();
            String ciphertext = resource.getCiphertext();
			
			// 你的Apiv3秘钥转换成Utf8的Byte
            byte[] aesKey = WeChatPayCostant.API_V3_SECRET.trim().toLowerCase().getBytes("UTF-8");
            // 以下微信传来的附加值(很重要)
            byte[] associatedDataBytes = associatedData.getBytes("UTF-8");
            byte[] nonceBytes = nonce.getBytes("UTF-8");
            byte[] ciphertextBytes = Base64.decodeBase64(ciphertext);

            // 开始解密 解密工具类请参照微信官网文档
            // https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/zheng-shu-he-hui-tiao-bao-wen-jie-mi
            AesUtil aesUtil = new AesUtil(aesKey);
            
            // 这里所需要的的工具类在文末
            String decryptedString = WxUtil.decryptToString(associatedDataBytes, nonceBytes, ciphertextBytes);
            log.info("微信支付回调 - 解密: {}", decryptedString);
            // 解密得到的json结果
            JSONObject decryptedJsonObj = JSONObject.parseObject(decryptedString);
            
        } catch (Exception e) {
            log.error("支付回调失败: {}", e.getMessage());
            e.printStackTrace();

            resCode = "FAIL";
            resMessage = "支付失败";
        }

        JSONObject returnJson = new JSONObject();
        returnJson.put("code", resCode);
        returnJson.put("message", resMessage);

        return returnJson.toJSONString();
    }
getRequestBody()方法;
/**
     * 获取请求体
     * @param request 请求
     */
    private String getRequestBody(HttpServletRequest request) {
        ServletInputStream stream = null;
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        try {
            stream = request.getInputStream();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            assert stream != null;
            try {
                stream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            assert reader != null;
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return sb.toString();
    }

/*
* 微信工具类
*/
public class WxUtil{

	private static final int KEY_LENGTH_BYTE = 32;
    private static final int TAG_LENGTH_BIT = 128;


 /**
     * 解密
     * @param associatedData 附加数据
     * @param nonce 随机串
     * @param ciphertext 数据密文
     * @param aesKey apiv3秘钥
     * @return 结果
     * @throws Exception 异常
     */
    public static String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext,byte[] aesKey) throws Exception {
        try {
            if (aesKey.length != KEY_LENGTH_BYTE) {
                throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
            }

            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            if(associatedData != null && associatedData.length > 0) {
                cipher.updateAAD(associatedData);
            }else {
                cipher.updateAAD("".getBytes("utf-8"));
            }

            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");

        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }


}

以上就是本文的全部内容,希望对大家的学习有所帮助

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值