.NET Core微信支付V3签名生成和验证签名
写在前面的话
1、签名生成:当请求微信支付API时,签名不通过,无法使用API接口(使用API证书私钥加密)
官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
2、验证签名:当微信支付回调的时,校验微信请求的签名,防止恶意请求(使用平台证书公钥解密)
官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml
3、签名生成微信官方给了示例代码,可以直接使用(但是是写死私钥的,本文是从证书.p12文件中读取);验证签名微信官方没有给.NET的示例代码
一、签名生成
源码:
/// <summary> /// 构造签名串 /// </summary> /// <param name="method">HTTP请求方式(全大写)</param> /// <param name="body">API接口请求参数的json字符串</param> /// <param name="uri">API接口的相对路径</param> /// <returns></returns> protected string BuildAuthAsync(string method, string body, string uri) { var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); string nonce = Path.GetRandomFileName(); string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n"; string signature = RequestSign(message); return $"mchid=\"{_mchID}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{_serialNo}\",signature=\"{signature}\""; } /// <summary> /// 生成签名 /// </summary> /// <param name="message"></param> /// <returns></returns> protected string RequestSign(string message) { //加载证书 _apiCertPath API证书物理路径 _certPwd API证书密码(默认是商户号) X509Certificate2 cer = new X509Certificate2(_apiCertPath, _certPwd, X509KeyStorageFlags.Exportable); if (cer != null) { RSA rsa = cer.GetRSAPrivateKey(); //获取私钥 //查看在不同平台上的具体类型 byte[] data = Encoding.UTF8.GetBytes(message); return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)); } else { return ""; } }
签名使用: 请求API接口时,通过HTTP的Authorization头传递签名
二、验证签名
源码:
/// <summary> /// 验证签名 /// </summary> /// <param name="serialNumber">证书序列号</param> /// <param name="timestamp">时间戳</param> /// <param name="nonce">随机串</param> /// <param name="body"></param> /// <param name="sign">签名</param> /// <returns></returns> public bool SignVerify(string serialNumber, string timestamp, string nonce, string body, string sign) { try { string message = $"{timestamp}\n{nonce}\n{body}\n"; string certPath = string.Format(_wechatCertPath, serialNumber); //certPath 平台证书路径 if (!File.Exists(certPath)) { LogHelper.Error($"平台证书不存在:{certPath},证书序列号:{serialNumber}", LogHelper.GetCurSourceFileName(), LogHelper.GetLineNum()); return false; } X509Certificate2 cert = new X509Certificate2(certPath); //加载平台证书 return SignVerify(cert, timestamp, nonce, body, sign); //重载方法 } catch (Exception ex) { LogHelper.Error($"签名验证出现异常:{ex.Message}", LogHelper.GetCurSourceFileName(), LogHelper.GetLineNum()); return false; } } /// <summary> /// 验证签名 /// </summary> /// <param name="cert">证书对象</param> /// <param name="timestamp">时间戳</param> /// <param name="nonce">随机串</param> /// <param name="body"></param> /// <param name="sign">签名</param> /// <returns></returns> public bool SignVerify(X509Certificate2 cert, string timestamp, string nonce, string body, string sign) { try { string message = $"{timestamp}\n{nonce}\n{body}\n"; if (cert != null) { byte[] data = Encoding.UTF8.GetBytes(message); var rsaParam = cert.GetRSAPublicKey().ExportParameters(false); var rsa = new RSACryptoServiceProvider(); rsa.ImportParameters(rsaParam); var isOk = rsa.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), Convert.FromBase64String(sign)); if (!isOk) { LogHelper.Error($"签名校验失败:", LogHelper.GetCurSourceFileName(), LogHelper.GetLineNum()); return false; } return true; } else { LogHelper.Error($"证书对象X509Certificate2为空", LogHelper.GetCurSourceFileName(), LogHelper.GetLineNum()); return false; } } catch (Exception ex) { LogHelper.Error($"签名验证出现异常:{ex.Message}", LogHelper.GetCurSourceFileName(), LogHelper.GetLineNum()); return false; } }
验签过程:
1、根据回调参数的平台证书序列号判断,当前本地证书是否有效,如无效,需要重新获取证书(可以参考上一篇文章:.NET Core微信支付V3平台证书下载(包含签名验证))
2、再进行签名验证
写在结尾的话
之前开发过程中被验证签名困扰过,所以记录一下,方便日后查阅
对于本文有疑问可以联系我(1217445199@qq.com),欢迎交流~
转载请注明出处,谢谢~
本文来自博客园,作者:汪小让,转载请注明原文链接:https://www.cnblogs.com/wangxiaorang/p/15711235.html