我正在开发一个微信里H5支付功能,目前被不能解密通知报文折磨1个星期了,求支援,
我的代码在最下面
运行到这一步报错
byte[] combinedCiphertext = new byte[ciphertextBytes.Length + tag.Length];
Buffer.BlockCopy(ciphertextBytes, 0, combinedCiphertext, 0, ciphertextBytes.Length);
Buffer.BlockCopy(tag, 0, combinedCiphertext, ciphertextBytes.Length, tag.Length);
报错和日志如下,
2025-06-22 10:13:44.934 +08:00 [INF] APIv3密钥指纹: F6EE716DF91A4CAC
2025-06-22 10:13:44.936 +08:00 [INF] 分离后的密文长度: 423, 标签: 1429155F665DC3D543A1FE8DB52B0661
2025-06-22 10:13:44.936 +08:00 [INF] 关联数据为 null,使用空字节数组
2025-06-22 10:13:44.948 +08:00 [INF] 解密前的参数 - Key: 3931333239353433303130383539343830343139383938343533373730393738, Nonce: k2Nhn3Yjg2cv, AD: , , Tag: 1429155F665DC3D543A1FE8DB52B0661
2025-06-22 10:13:50.156 +08:00 [ERR] ---------- RemoteServiceErrorInfo ----------
{
"code": null,
"message": "对不起,在处理您的请求期间产生了一个服务器内部错误!!",
"details": null,
"data": {},
"validationErrors": null
}
2025-06-22 10:13:50.157 +08:00 [ERR] mac check in GCM failed
Org.BouncyCastle.Crypto.InvalidCipherTextException: mac check in GCM failed
at Org.BouncyCastle.Crypto.Modes.GcmBlockCipher.DoFinal(Span`1 output)
at Org.BouncyCastle.Crypto.Modes.GcmBlockCipher.DoFinal(Byte[] output, Int32 outOff)
at LaborMall.WeChatPay.WeChatPayV3Service.DecryptWithDotNet(Byte[] key, Byte[] nonce, Byte[] ciphertextBytes, Byte[] tag, Byte[] associatedData) in E:\BSApp\mall\labormall-master\dev\aspnet-core\src\LaborMall.Application\WeChatPay\WeChatPayV3Service.cs:line 497
at LaborMall.WeChatPay.WeChatPayV3Service.AesGcmDecrypt(String associatedData, String nonce, String ciphertext) in E:\BSApp\mall\labormall-master\dev\aspnet-core\src\LaborMall.Application\WeChatPay\WeChatPayV3Service.cs:line 467
at LaborMall.WeChatPay.WeChatPayV3Service.Decrypt(WeChatPayEncryptedData encryptedData) in E:\BSApp\mall\labormall-master\dev\aspnet-core\src\LaborMall.Application\WeChatPay\WeChatPayV3Service.cs:line 362
at LaborMall.WeChatPay.WeChatPayV3Service.GetNonce() in E:\BSApp\mall\labormall-master\dev\aspnet-core\src\LaborMall.Application\WeChatPay\WeChatPayV3Service.cs:line 272
at LaborMall.Communication.Controllers.WeChatPayController.PayNotification2Async(String input) in E:\BSApp\mall\labormall-master\dev\aspnet-core\hosts\LaborMall.Communication.Host\Controllers\WeChatPayController.cs:line 133
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
以下是源码
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using LaborMall.WeChatPay.Dtos;
using Aliyun.Credentials.Http;
using System.IO;
using Volo.Abp;
using LaborMall.Data;
using LaborMall.Favorites;
using Microsoft.Extensions.Logging;
using static System.Runtime.InteropServices.JavaScript.JSType;
using Quartz.Logging;
using Microsoft.IdentityModel.Tokens;
using System.Text.RegularExpressions;
using System.Text.Unicode;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Engines;
using Quartz.Util;
using Org.BouncyCastle.Utilities;
using System.Net;
using Org.BouncyCastle.Crypto;
namespace LaborMall.WeChatPay
{ public class WeChatPayV3Service
{
private readonly string _apiUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
private readonly string _oauthUrl = "https://api.weixin.qq.com/sns/oauth2/access_token";
private readonly string _mchId; // 商户号
private readonly string _appId; // 公众号AppId
private readonly string _appSecret; // 公众号AppSecret
private readonly string _apiV3Key; // APIv3密钥
private readonly ICertificateService _certificateService;
private readonly string _serialNo; // 商户证书序列号
private readonly HttpClient _httpClient;
private readonly ILogger<WeChatPayV3Service> _logger;
public WeChatPayV3Service(
string mchId,
string appId,
string appSecret,
string apiV3Key,
ICertificateService certificateService, // 先注入证书服务
string serialNo,
ILogger<WeChatPayV3Service> logger,
HttpClient httpClient = null)
{
_mchId = mchId;
_appId = appId;
_appSecret = appSecret;
_apiV3Key = apiV3Key;
_certificateService = certificateService;
_serialNo = serialNo;
_httpClient = httpClient ?? new HttpClient();
_logger = logger;
}
/// <summary>
/// 通过code获取用户OpenId
/// </summary>
public async Task<string> GetUserOpenIdAsync(string code)
{
if (string.IsNullOrEmpty(code))
{
throw new ArgumentException("授权码不能为空");
}
var url = $"{_oauthUrl}?appid={_appId}&secret={_appSecret}&code={HttpUtility.UrlEncode(code)}&grant_type=authorization_code";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var tokenResponse = JObject.Parse(content);
var openId = tokenResponse["openid"]?.ToString();
if (string.IsNullOrEmpty(openId))
{
throw new Exception("获取OpenId失败");
}
return openId;
}
/// <summary>
/// JSAPI下单
/// </summary>
/// <summary>
/// JSAPI下单
/// </summary>
public async Task<string> CreateJsApiOrderAsync(
string openId,
string outTradeNo,
string description,
int totalAmount,
string notifyUrl,
string attach = null)
{
try
{
// 1. 构建请求体
var body = new
{
appid = _appId,
mchid = _mchId,
description = description,
out_trade_no = outTradeNo,
notify_url = notifyUrl,
amount = new { total = totalAmount, currency = "CNY" },
payer = new { openid = openId },
attach = attach,
time_expire = DateTime.UtcNow.AddMinutes(30).ToString("yyyy-MM-ddTHH:mm:sszzz")
};
string bodyJson = JsonConvert.SerializeObject(body);
_logger.LogInformation($"请求体JSON: {bodyJson}");
// 2. 生成签名并发送请求
var request = new HttpRequestMessage(HttpMethod.Post, _apiUrl);
request.Content = new StringContent(bodyJson, Encoding.UTF8, "application/json");
string timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
string nonce = Guid.NewGuid().ToString("N");
_logger.LogInformation($"时间戳: {timestamp}");
_logger.LogInformation($"随机数: {nonce}");
// 使用新的API请求签名方法
string signature = GenerateApiRequestSignature(request.Method.Method, _apiUrl, timestamp, nonce, bodyJson);
string authHeader = GetAuthHeader(timestamp, nonce, signature);
request.Headers.Add("Authorization", $"WECHATPAY2-SHA256-RSA2048 {authHeader}");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Wechatpay-Serial", _serialNo);
request.Headers.Add("User-Agent", "LaborMallApp/1.0");
// 记录完整的请求头信息
_logger.LogInformation("请求头信息:");
foreach (var header in request.Headers)
{
_logger.LogInformation($"{header.Key}: {string.Join(", ", header.Value)}");
}
// 3. 发送请求并解析响应
var response = await _httpClient.SendAsync(request);
string responseJson = await response.Content.ReadAsStringAsync();
_logger.LogInformation($"响应状态码: {response.StatusCode}");
_logger.LogInformation($"响应内容: {responseJson}");
if (!response.IsSuccessStatusCode)
throw new Exception($"微信支付请求失败: {response.StatusCode}\n{responseJson}");
dynamic result = JsonConvert.DeserializeObject(responseJson);
return result.prepay_id;
}
catch (Exception ex)
{
_logger.LogError(ex, "JSAPI下单过程中发生错误");
throw;
}
}
/// <summary>
/// 生成前端调起支付参数
/// </summary>
public Dictionary<string, string> GetJsApiSdkParams(string prepayId)
{
string timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
string nonceStr = Guid.NewGuid().ToString("N");
string package = $"prepay_id={prepayId}";
string message = $"{_appId}\n{timestamp}\n{nonceStr}\n{package}\n";
string paySign = GenerateSignature(message); // 修改后的签名方法
return new Dictionary<string, string>
{
{ "appId", _appId },
{ "timeStamp", timestamp },
{ "nonceStr", nonceStr },
{ "package", package },
{ "signType", "RSA" },
{ "paySign", paySign }
};
}
/// <summary>
/// 生成API请求签名 (用于微信支付V3 API请求)
/// </summary>
private string GenerateApiRequestSignature(string method, string url, string timestamp, string nonce, string body)
{
try
{
// 规范化URL路径
var uri = new Uri(url);
string path = uri.AbsolutePath;
// 构造规范化的签名消息
string message = $"{method.ToUpperInvariant()}\n{path}\n{timestamp}\n{nonce}\n{body}\n";
_logger.LogInformation($"生成API请求签名的消息内容:\n{message}");
return GenerateSignature(message);
}
catch (Exception ex)
{
_logger.LogError(ex, "生成API请求签名时发生错误");
throw;
}
}
/// <summary>
/// 基础签名方法 (实际执行RSA签名)
/// </summary>
private string GenerateSignature(string message)
{
using var rsa = _certificateService.LoadPrivateKey();
byte[] data = Encoding.UTF8.GetBytes(message);
byte[] signature = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
string base64Sign = Convert.ToBase64String(signature);
_logger.LogInformation($"生成的签名: {base64Sign}");
return base64Sign;
}
private bool VerifySignature(string message, string signature, string privateKey)
{
using (var rsa = RSA.Create())
{
rsa.ImportFromPem(privateKey);
return rsa.VerifyData(
Encoding.UTF8.GetBytes(message),
Convert.FromBase64String(signature),
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
}
}
private string GetAuthHeader(string timestamp, string nonce, string signature)
{
// 确保所有值都是ASCII编码
var authValues = new Dictionary<string, string>
{
{ "mchid", _mchId },
{ "nonce_str", nonce },
{ "timestamp", timestamp },
{ "serial_no", _serialNo },
{ "signature", signature }
};
var sb = new StringBuilder();
foreach (var kv in authValues)
{
if (sb.Length > 0)
sb.Append(",");
sb.Append($"{kv.Key}=\"{kv.Value}\"");
}
return sb.ToString();
}
//////////////////////////////////////////////////////////////通知处理部分、、、、、、、、、、、、、、、、、、、
public void GetNonce()
{
// 测试
// var decryptor = new WeChatPayV3Decryptor("你的真实APIv3密钥", logger);
// { "id":"fc46e8ac-c0b6-5397-93f8-c857106144f0","create_time":null,"event_type":null,"resource_type":null,"resource":{ "original_type":null,"algorithm":"AEAD_AES_256_GCM","ciphertext":"INPe4z+FBIOoIatLGoNMmVtdrD8GD/Ic9u/mrzgyjNcEgH81EXl690d5XqeZlWRc0kORrWJdMWeF0C3ir7Farg9MsR1FRmZv+ULFsEtbKkzTVfn87gBqFLNauJIVj1uAwSqPF9wHCt3NdB+71i8Jpb6Fa8Lovk/n1KDCd5oDyB7d5NZ7Pf3+Z5lBM64I7aiaoiRPE9dmkl9odTpM6yXdOa7iDaGSe7pqob4y0btE+siRqrH9lnfEqHv2rnxuA7+Ma1I/gd8lHCnNjSwPOCXF1N0V/TVbvg2wH4yIldvhafkWwi+FVstfl9APQj+2HRTk705ULnMcs7Hn3ySL3utE6fJtQnUgJPEG8x397ZjcSSQtO8+r+rJdOuevx7FKHAzeOP0ny/0fD45Eig07LoGnlwbPmDugaKc5U080XFbploOGxLoTW8jFpFAQYBJLldmLjMpX30GzyqTZxx9T6LlumO/vc0Cy6ebUrz7iOYjMmAIruFkJGvj4M+VgJWk95JUnwNJZHKbBDT+Gu4QaS/OA9/ZjJM+Fq2VPZhLyYlc0uP7svCIwMmz7R6iKb+zevUarst1aGRMTFA==","nonce":"sb51ZQpI9N43","associated_data":null},"summary":"支付成功"}
// 处理回调
var decryptedData = Decrypt(new WeChatPayEncryptedData
{
Algorithm = "AEAD_AES_256_GCM",
Ciphertext = "bDEfmqkHlnR/h9GoUXmNtrdOZYse/hoY0EezSkx+esohSrm55riylrQIWaANNPTWk/bvEkIH9Gt7I0U3LTDfKjnVt3Y0MfAups1WwQF4dBB9juOCwUHLoYS4kpI8FOq0UAx+OIkEOC4e21uKdRlkxi9Y6V6jxgCEpz53+4pZ64CeCFboPcBxyQFOjd0dUqK3T4X2unBIuyXbs3FbjWDyRbashR7diXhjzUbO8OWrM/lN5VBvUGXtJb2mYRy4OOKC1Z0mL8t1zCecvx3wqc0xUVu4L0TI5Af2ma/4Jiz09v5dBhjiduOFClGPPgBKMnpyNnzEMldLVsrYnPxjsQkPW+2NCel5UMEz/W/JfD+MidlcUM9UTzMPZh1ZMkAQnjIDn2mzHh0dhLQF7y998ZHwccsMOo5bSvYTPSGDjyy/5S6MczUpjaZRlaDs0Hyz/Mzmaq2H7XSzGRWdp3/Hdb3p/VNLYvHvHQnsMWKGRq7LUB+GnLaHSSXiDEg0p8irl+XkcpVO007MrPOnbv6K7uGYaAVIBs2urJKA7H7ul45Fa2UnpwydhEEKFCkVX2Zdw9VDof6NtSsGYQ==",
Nonce = "k2Nhn3Yjg2cv",
AssociatedData = null
});
// 处理业务逻辑
Console.WriteLine($"订单号: {decryptedData.OutTradeNo}, 金额: {decryptedData.Amount.Total}");
}
/// <summary>
/// 处理微信支付通知
/// </summary>
[RemoteService(false)]
public async Task<AppResult<string>> HandlePaymentNotificationAsync(WeChatPayNotifyRequestDto input)
{
try
{
// 1. 记录原始回调数据
string rawJson = JsonConvert.SerializeObject(input);
_logger.LogInformation($"收到支付回调: {rawJson}");
// 2. 验证数据结构
if (input?.Resource == null)
throw new ArgumentNullException("Resource");
// 3. 解密数据
WeChatPayDecryptedDataDto decryptedJson = Decrypt(input.Resource);
// 4. 验证解密结果
if (string.IsNullOrEmpty(decryptedJson.OutTradeNo))
throw new Exception("解密结果为空");
// 5. 返回成功响应
return new AppResult<string>
{
Success = true,
Data = decryptedJson.OutTradeNo,
};
}
catch (Exception ex)
{
_logger.LogError(ex, "处理支付回调失败");
return new AppResult<string>
{
Success = false,
Message = ex.Message
};
}
}
private async Task ProcessPaymentResultAsync(WeChatPayDecryptedDataDto data)
{
// 根据支付结果更新业务状态
// 例如:更新订单状态为已支付
_logger.LogInformation($"处理微信支付结果: 订单号={data.OutTradeNo}, 交易状态={data.TradeState}");
// 这里可以添加你的业务逻辑
// 例如:var order = await _orderRepository.FindAsync(x => x.OrderNo == data.OutTradeNo);
// if (order != null) { ... }
}
/// <summary>
/// 解密微信支付回调数据(最终版)
/// </summary>
public WeChatPayDecryptedDataDto Decrypt(WeChatPayEncryptedData encryptedData)
{
// encryptedData.AssociatedData = encryptedData.AssociatedData ?? "resource_type=encrypt-resource";
// 1. 验证并修复输入数据
if (encryptedData == null) throw new ArgumentNullException(nameof(encryptedData));
//var fixedData = ValidateAndFixEncryptedData(encryptedData);
// _logger.LogInformation($"解密参数 - Nonce: {fixedData.Nonce}, AssociatedData: {fixedData.AssociatedData}, Ciphertext长度: {fixedData.Ciphertext.Length}");
string decryptedJson = AesGcmDecrypt(encryptedData.AssociatedData, encryptedData.Nonce, encryptedData.Ciphertext);
// 6. 解析结果
_logger.LogDebug($"解密成功,原始JSON: {decryptedJson}");
return JsonConvert.DeserializeObject<WeChatPayDecryptedDataDto>(decryptedJson);
}
/// <summary>
/// 证书/回调报文解密
/// </summary>
/// <param name="associatedData"></param>
/// <param name="nonce"></param>
/// <param name="ciphertext"></param>
/// <returns></returns>
public string AesGcmDecrypt(string associatedData, string nonce, string ciphertext)
{
// 1. 严格验证密钥格式
if (string.IsNullOrEmpty(_apiV3Key))
throw new ArgumentNullException(nameof(_apiV3Key));
if (_apiV3Key.Length != 32)
throw new ArgumentException($"APIv3密钥长度必须为32字节, 当前长度: {_apiV3Key.Length}");
// 3. 记录密钥指纹用于验证
using (var sha256 = SHA256.Create())
{
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(_apiV3Key));
string keyFingerprint = BitConverter.ToString(hash).Replace("-", "").Substring(0, 16);
_logger.LogInformation($"APIv3密钥指纹: {keyFingerprint}");
}
// 4. 增强Base64处理
string sanitizedCiphertext = ciphertext;
// 4.1 移除所有非Base64字符
sanitizedCiphertext = Regex.Replace(sanitizedCiphertext, @"[^a-zA-Z0-9\+/\-_=]", "");
// 4.2 处理URL编码字符
if (sanitizedCiphertext.Contains('%'))
{
try
{
sanitizedCiphertext = WebUtility.UrlDecode(sanitizedCiphertext);
_logger.LogInformation("URL解码后: " + sanitizedCiphertext);
}
catch (Exception decodeEx)
{
_logger.LogWarning($"URL解码失败: {decodeEx.Message}");
}
}
// 4.3 标准化Base64字符
sanitizedCiphertext = sanitizedCiphertext
.Replace('_', '/')
.Replace('-', '+');
// 4.4 添加Base64填充
int mod4 = sanitizedCiphertext.Length % 4;
if (mod4 > 0) sanitizedCiphertext += new string('=', 4 - mod4);
// 5. 尝试Base64解码
byte[] ciphertextBytes;
ciphertextBytes = Convert.FromBase64String(sanitizedCiphertext);
// 6. 分离加密数据和认证标签
if (ciphertextBytes.Length < 16)
throw new ArgumentException("密文太短,无法包含认证标签");
// 最后16字节是认证标签
int ciphertextLength = ciphertextBytes.Length - 16;
byte[] tag = new byte[16];
byte[] actualCiphertext = new byte[ciphertextLength];
// 复制前 N-16 字节为实际密文
Buffer.BlockCopy(ciphertextBytes, 0, actualCiphertext, 0, ciphertextLength);
// 复制最后16字节为标签
Buffer.BlockCopy(ciphertextBytes, ciphertextLength, tag, 0, 16);
_logger.LogInformation($"分离后的密文长度: {actualCiphertext.Length}, 标签: {BitConverter.ToString(tag).Replace("-", "")}");
// 7. 准备参数
byte[] key = Encoding.UTF8.GetBytes(_apiV3Key);
byte[] nonceBytes = Encoding.UTF8.GetBytes(nonce);
//byte[] adBytes = string.IsNullOrEmpty(associatedData) ? new byte[0] : Encoding.UTF8.GetBytes(associatedData ?? "");
// 处理关联数据:区分 null 和空字符串
byte[] adBytes;
if (associatedData == null)
{
_logger.LogInformation("关联数据为 null,使用空字节数组");
adBytes = Array.Empty<byte>();
}
else
{
adBytes = Encoding.UTF8.GetBytes(associatedData);
}
// 8. 使用.NET原生库解密
return DecryptWithDotNet(key, nonceBytes, ciphertextBytes, tag, adBytes);
}
/// <summary>
/// 使用.NET原生库进行解密
/// </summary>
private string DecryptWithDotNet(byte[] key, byte[] nonce, byte[] ciphertextBytes, byte[] tag, byte[] associatedData)
{
try
{
// byte[] plaintext = new byte[ciphertext.Length];
_logger.LogInformation($"解密前的参数 - Key: {BitConverter.ToString(key).Replace("-", "")}, Nonce: {Encoding.UTF8.GetString(nonce)}, AD: {Encoding.UTF8.GetString(associatedData)}, , Tag: {BitConverter.ToString(tag).Replace("-", "")}");
GcmBlockCipher gcmBlockCipher = new(new AesEngine());
AeadParameters aeadParameters = new(
new KeyParameter(Encoding.UTF8.GetBytes(_apiV3Key)),
128,
nonce, //Encoding.UTF8.GetBytes(nonce),
associatedData);// Encoding.UTF8.GetBytes(associatedData));
gcmBlockCipher.Init(false, aeadParameters);
// 2. 合并密文和认证标签(BouncyCastle要求)
byte[] combinedCiphertext = new byte[ciphertextBytes.Length + tag.Length];
Buffer.BlockCopy(ciphertextBytes, 0, combinedCiphertext, 0, ciphertextBytes.Length);
Buffer.BlockCopy(tag, 0, combinedCiphertext, ciphertextBytes.Length, tag.Length);
// 3. 执行解密
byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(combinedCiphertext.Length)];
int len = gcmBlockCipher.ProcessBytes(combinedCiphertext, 0, combinedCiphertext.Length, plaintext, 0);
gcmBlockCipher.DoFinal(plaintext, len);
_logger.LogInformation($"解密成功,明文: {Encoding.UTF8.GetString(plaintext)}");
return Encoding.UTF8.GetString(plaintext);
}
catch (AuthenticationTagMismatchException ex)
{
// 详细记录错误信息
_logger.LogError($"认证标签不匹配: " +
$"Key: {BitConverter.ToString(key).Replace("-", "").Substring(0, 8)}..., " +
$"Nonce: {Encoding.UTF8.GetString(nonce)}, " +
$"AD: {Encoding.UTF8.GetString(associatedData)}, " +
$"Ciphertext length: {ciphertextBytes.Length}, " +
$"Tag: {BitConverter.ToString(tag).Replace("-", "")}");
throw new WeChatPayDecryptionException("解密失败: 认证标签不匹配", ex);
}
}
}
public class WeChatPayDecryptionException : Exception
{
public WeChatPayDecryptionException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
"associated_data":null这个不能为空的。解密需要byte[] associatedData,byte[] nonce,String ciphertext,以及32位的商户密钥 参考如下:https://pay.weixin.qq.com/doc/v3/merchant/4012071382 微信发的回调body也是有值的,完整格式如下: {"id":"da2b8a7f-dad3-5f69-b5eb-2446b94543","create_time":"2024-08-16T15:00:03+08:00","resource_type":"encrypt-resource","event_type":"TRANSACTION.SUCCESS","summary":"支付成功","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"Zkwzt1znCk78U2/xRl2ZVHlwaxcTg02MWN/36VW3cTFGJJWCDucckbqEPFCt5HIAwuhz1+oVbI1NJDqqmdfJAKSeZvDWivzgkLehZSI211qZhPruF8+/Oo8fuEmTigQo6BL1XUDUKSyegcYphyU+hFayU6gXAa7hatmbOr36dALy5Ijb+o6f+VDJ33A77lGgFP5xD1IhaqLkaYnDseBxot5bn3fwtASpNN176VKnKDb4co5E73kJzgtF3qPZTv40Nud0qakFHjs8h7NI7/hKKZVRL8wd3NHXLFfWGlq11GBjpH4Pm7OtZMifdHEa2pxZDiiYnAdO49cx1EMToN+0cdKv4dxWLxsy4wJhja44zaIfaViGSxbOojo1017C1eNQvfLraEqRnASvkilb/MQUPhLNM93PI1I20M6rrsKMNsF0U3IKYHvay+r9V+twrzXuHbgBWWVUtxVwvpIEzuPkJ756zRK5mf/XOLyotW4E42DBVgJZ+11OyTY8aSANWUl2G4zJoVlQJgHgNy0JNF2w0/7kU02o6fkDp81lOP7QU/4PeyzIqchDoqqhxKGOfMrun+V6/avn+tcZ2qIK+2etIZ4NwTun6G/Q2Cn8hRUwcXU28eWJvMrFeg==","associated_data":"transaction","nonce":"Oiyu5riw0HOJ"}}
你的associated_data为什么没有值呢?
2025-06-22 11:27:04.639 +08:00 [INF] 收到支付回调: {"id":"baf703ab-dbea-5293-9c8e-371145be20e0","create_time":null,"event_type":null,"resource_type":null,"resource":{"original_type":null,"algorithm":"AEAD_AES_256_GCM","ciphertext":"ztv2nfR3NtzUZ2YG3pBQlxZwOsng693/90z9FfQiQwpzgQEJxLdnBOXAcpPm3SyJr6CwmAG5F4x5LVihgiSvXe9K1tdWOG9mKapdiwOuka7toBaSmtvo2DT0IrAwOnqKTxpyWcCDLUQMLinrxNVoGccL0rRo5RZjAkb0bcPc/7qiC4cSVZB21Lh1P2Gc6VIts3K1REfu/vaczkIfi4MIH8aFkP64n4FElmdiztXzlpMbE3EQUP44cDebLCP8oLlHt3uPwnpjUtaTN3CaBe7Sdw0bGdWTl6HcA1sflNcMJt8b8oZgx0QcjzqP4bzWpm58JXVCv5cMPjgK6c6MgNSdgu0TIcA72J+uS62FahxoSGhpPY7NrxsAwiNsMZ6CsbGzED3jlOiG1qRDfY5a3e/K/B0wuepAfSZPKouCRFB2VZ0CoEmdtDB0tnTqpVKOWWxAHzudP9TYxwylYgwqQTGnYst1m3WrogV+SYg8hblbUGNL6ws21csBI5Ygo1GY2YR5mleDUQ3pjb6x1UhZQTzbzZkdFnvVKJEUdhov2uoxRxKOBS5zc7BSrT4xEKpPuAUB9z8N9Jappg==","nonce":"FrUTbzH9bpvp","associated_data":null},"summary":"支付成功"}
2025-06-22 11:27:04.640 +08:00 [INF] APIv3密钥指纹: F6EE716DF91A4CAC
2025-06-22 11:27:04.641 +08:00 [INF] 分离后的密文长度: 423, 标签: AD3E3110AA4FB80501F73F0DF496A9A6
2025-06-22 11:27:04.641 +08:00 [INF] 关联数据为 null,使用空字节数组
2025-06-22 11:27:04.663 +08:00 [INF] 解密前的参数 - Key: 3931333239353433303130383539343830343139383938343533373730393738, Nonce: FrUTbzH9bpvp, AD: , , Tag: AD3E3110AA4FB80501F73F0DF496A9A6
2025-06-22 11:27:04.704 +08:00 [ERR] 处理支付回调失败
Org.BouncyCastle.Crypto.InvalidCipherTextException: mac check in GCM failed
at Org.BouncyCastle.Crypto.Modes.GcmBlockCipher.DoFinal(Span`1 output)
at Org.BouncyCastle.Crypto.Modes.GcmBlockCipher.DoFinal(Byte[] output, Int32 outOff)
at LaborMall.WeChatPay.WeChatPayV3Service.DecryptWithDotNet(Byte[] key, Byte[] nonce, Byte[] ciphertextBytes, Byte[] tag, Byte[] associatedData) in E:\BSApp\mall\labormall-master\dev\aspnet-core\src\LaborMall.Application\WeChatPay\WeChatPayV3Service.cs:line 496
at LaborMall.WeChatPay.WeChatPayV3Service.AesGcmDecrypt(String associatedData, String nonce, String ciphertext) in E:\BSApp\mall\labormall-master\dev\aspnet-core\src\LaborMall.Application\WeChatPay\WeChatPayV3Service.cs:line 467
at LaborMall.WeChatPay.WeChatPayV3Service.Decrypt(WeChatPayEncryptedData encryptedData) in E:\BSApp\mall\labormall-master\dev\aspnet-core\src\LaborMall.Application\WeChatPay\WeChatPayV3Service.cs:line 362
at LaborMall.WeChatPay.WeChatPayV3Service.HandlePaymentNotificationAsync(WeChatPayNotifyRequestDto input) in E:\BSApp\mall\labormall-master\dev\aspnet-core\src\LaborMall.Application\WeChatPay\WeChatPayV3Service.cs:line 309
官方文档里就没有值呀