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

      转载请注明出处,谢谢~

  • 相关阅读:
    numpy通用函数
    机器学习之随机森林
    机器学习之支持向量机
    机器学习之逻辑回归
    机器学习之决策树
    机器学*之K*邻
    机器学习之线性回归
    模型之各个指标
    模型之信息熵
    模型之决策树
  • 原文地址:https://www.cnblogs.com/wangxiaorang/p/15711235.html
Copyright © 2020-2023  润新知