.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),欢迎交流~

  转载请注明出处,谢谢~

 

posted @ 2021-12-20 15:49  汪小让  阅读(3384)  评论(1编辑  收藏  举报