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

  轉載請注明出處,謝謝~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM