Explorar o código

[Alipay] 优化 CheckNotifySign、CheckResponseCertSignAsync

Roc %!s(int64=5) %!d(string=hai) anos
pai
achega
ef8111443c

+ 4 - 6
samples/WebApplicationSample/Controllers/NotifyController.cs

@@ -35,10 +35,7 @@ namespace WebApplicationSample.Controllers
         {
             try
             {
-                // 获取参数
-                var parameters = _client.GetParameters(Request);
-
-                parameters.TryGetValue("service", out var service);
+                var service = Request.Form["service"].ToString();
                 switch (service)
                 {
                     // 激活开发者模式
@@ -46,6 +43,8 @@ namespace WebApplicationSample.Controllers
                         {
                             var options = _optionsAccessor.Value;
 
+                            // 获取参数
+                            var parameters = _client.GetParameters(Request);
                             var sign = parameters["sign"];
                             parameters.Remove("sign");
 
@@ -61,7 +60,7 @@ namespace WebApplicationSample.Controllers
                         }
                 }
 
-                parameters.TryGetValue("msg_method", out var msg_method);
+                var msg_method = Request.Form["msg_method"].ToString();
                 switch (msg_method)
                 {
                     // 资金单据状态变更通知
@@ -218,7 +217,6 @@ namespace WebApplicationSample.Controllers
             }
         }
 
-
         private string MakeVerifyGWResponse(bool isSuccess, string certPublicKey, string appPrivateKey, string charset, string signType)
         {
             var xmlDoc = new XmlDocument(); //创建实例

+ 0 - 29
src/Essensoft.AspNetCore.Payment.Alipay/AlipayCertificateManager.cs

@@ -1,29 +0,0 @@
-using System.Collections.Concurrent;
-
-namespace Essensoft.AspNetCore.Payment.Alipay
-{
-    public class AlipayCertificateManager
-    {
-        /// <summary>
-        /// 不同支付宝公钥证书序列号对应的公钥内容
-        /// </summary>
-        private readonly ConcurrentDictionary<string, string> _SN2PublicKey = new ConcurrentDictionary<string, string>();
-
-        public bool IsEmpty => _SN2PublicKey.IsEmpty;
-
-        public bool Contains(string sn)
-        {
-            return _SN2PublicKey.ContainsKey(sn);
-        }
-
-        public bool TryAdd(string sn, string publicKey)
-        {
-            return _SN2PublicKey.TryAdd(sn, publicKey);
-        }
-
-        public bool TryGet(string sn, out string publicKey)
-        {
-            return _SN2PublicKey.TryGetValue(sn, out publicKey);
-        }
-    }
-}

+ 61 - 63
src/Essensoft.AspNetCore.Payment.Alipay/AlipayClient.cs

@@ -18,14 +18,14 @@ namespace Essensoft.AspNetCore.Payment.Alipay
     public class AlipayClient : IAlipayClient
     {
         private readonly IHttpClientFactory _httpClientFactory;
-        private readonly AlipayCertificateManager _certificateManager;
+        private readonly AlipayPublicKeyManager _alipayPublicKeyManager;
 
         #region AlipayClient Constructors
 
-        public AlipayClient(IHttpClientFactory httpClientFactory, AlipayCertificateManager certificateManager)
+        public AlipayClient(IHttpClientFactory httpClientFactory, AlipayPublicKeyManager certificateManager)
         {
             _httpClientFactory = httpClientFactory;
-            _certificateManager = certificateManager;
+            _alipayPublicKeyManager = certificateManager;
         }
 
         #endregion
@@ -433,81 +433,79 @@ namespace Essensoft.AspNetCore.Payment.Alipay
 
         private async Task CheckResponseCertSignAsync<T>(IAlipayRequest<T> request, string responseBody, bool isError, IAlipayParser<T> parser, AlipayOptions options) where T : AlipayResponse
         {
-            if (request is AlipayOpenAppAlipaycertDownloadRequest)
-            {
-                return;
-            }
-
             var certItem = parser.GetCertItem(request, responseBody);
             if (certItem == null)
             {
-                throw new AlipayException("sign check fail: Body is Empty!");
+                throw new AlipayException("cert check fail: Body is Empty!");
             }
 
-            if (!isError || isError && !string.IsNullOrEmpty(certItem.Sign))
+            if (!string.IsNullOrEmpty(certItem.CertSN))
             {
-                var currentAlipayPublicKey = await LoadAlipayPublicKeyAsync(certItem, options);
-                var rsaCheckContent = AlipaySignature.RSACheckContent(certItem.SignSourceDate, certItem.Sign, currentAlipayPublicKey, options.Charset, options.SignType);
-                if (!rsaCheckContent)
+                // 为空时添加本地支付宝公钥证书密钥
+                if (_alipayPublicKeyManager.IsEmpty)
                 {
-                    if (!string.IsNullOrEmpty(certItem.SignSourceDate) && certItem.SignSourceDate.Contains("\\/"))
-                    {
-                        var srouceData = certItem.SignSourceDate.Replace("\\/", "/");
-                        var jsonCheck = AlipaySignature.RSACheckContent(srouceData, certItem.Sign, currentAlipayPublicKey, options.Charset, options.SignType);
-                        if (!jsonCheck)
-                        {
-                            throw new AlipayException("sign check fail: check Sign and Data Fail JSON also");
-                        }
-                    }
-                    else
-                    {
-                        throw new AlipayException("sign check fail: check Sign and Data Fail!");
-                    }
+                    _alipayPublicKeyManager.TryAdd(options.AlipayPublicCertSN, options.AlipayPublicKey);
                 }
-            }
-        }
 
-        private async Task<string> LoadAlipayPublicKeyAsync(CertItem certItem, AlipayOptions options)
-        {
-            //为空时添加默认支付宝公钥证书密钥
-            if (_certificateManager.IsEmpty)
-            {
-                _certificateManager.TryAdd(options.AlipayPublicCertSN, options.AlipayPublicKey);
-            }
-
-            //如果响应的支付宝公钥证书序号已经缓存过,则直接使用缓存的公钥
-            if (_certificateManager.TryGet(certItem.CertSN, out var publicKey))
-            {
-                return publicKey;
-            }
+                // 如果返回的支付宝公钥证书序列号与本地支付宝公钥证书序列号不匹配,通过返回的支付宝公钥证书序列号去网关拉取新的支付宝公钥证书
+                if (!_alipayPublicKeyManager.ContainsKey(certItem.CertSN))
+                {
+                    var model = new AlipayOpenAppAlipaycertDownloadModel
+                    {
+                        AlipayCertSn = certItem.CertSN
+                    };
 
-            //否则重新下载新的支付宝公钥证书并更新缓存
-            var model = new AlipayOpenAppAlipaycertDownloadModel
-            {
-                AlipayCertSn = certItem.CertSN
-            };
+                    var req = new AlipayOpenAppAlipaycertDownloadRequest();
+                    req.SetBizModel(model);
 
-            //下载应用支付宝公钥证书
-            var request = new AlipayOpenAppAlipaycertDownloadRequest();
-            request.SetBizModel(model);
+                    var response = await CertificateExecuteAsync(req, options);
+                    if (response.IsError)
+                    {
+                        throw new AlipayException("支付宝公钥证书校验失败,请确认是否为支付宝签发的有效公钥证书");
+                    }
 
-            var response = await CertificateExecuteAsync(request, options);
-            if (response.IsError)
-            {
-                throw new AlipayException("支付宝公钥证书校验失败,请确认是否为支付宝签发的有效公钥证书");
-            }
+                    if (!AntCertificationUtil.IsTrusted(response.AlipayCertContent, options.RootCert))
+                    {
+                        throw new AlipayException("支付宝公钥证书校验失败,请确认是否为支付宝签发的有效公钥证书");
+                    }
 
-            if (!AntCertificationUtil.IsTrusted(response.AlipayCertContent, options.RootCert))
-            {
-                throw new AlipayException("支付宝公钥证书校验失败,请确认是否为支付宝签发的有效公钥证书");
-            }
+                    var alipayCert = AntCertificationUtil.ParseCert(response.AlipayCertContent);
+                    var alipayCertSN = AntCertificationUtil.GetCertSN(alipayCert);
+                    var alipayCertPublicKey = AntCertificationUtil.ExtractPemPublicKeyFromCert(alipayCert);
 
-            var alipayCert = AntCertificationUtil.ParseCert(response.AlipayCertContent);
-            var alipayCertSN = AntCertificationUtil.GetCertSN(alipayCert);
-            var newAlipayPublicKey = AntCertificationUtil.ExtractPemPublicKeyFromCert(alipayCert);
-            _certificateManager.TryAdd(alipayCertSN, newAlipayPublicKey);
+                    _alipayPublicKeyManager.TryAdd(alipayCertSN, alipayCertPublicKey);
+                }
 
-            return newAlipayPublicKey;
+                // 针对成功结果且有支付宝公钥的进行验签
+                if (_alipayPublicKeyManager.TryGetValue(certItem.CertSN, out var alipayPublicKey))
+                {
+                    if (!isError || isError && !string.IsNullOrEmpty(certItem.Sign))
+                    {
+                        var rsaCheckContent = AlipaySignature.RSACheckContent(certItem.SignSourceDate, certItem.Sign, alipayPublicKey, options.Charset, options.SignType);
+                        if (!rsaCheckContent)
+                        {
+                            // 针对JSON \/问题,替换/后再尝试做一次验证
+                            if (!string.IsNullOrEmpty(certItem.SignSourceDate) && certItem.SignSourceDate.Contains("\\/"))
+                            {
+                                var srouceData = certItem.SignSourceDate.Replace("\\/", "/");
+                                var jsonCheck = AlipaySignature.RSACheckContent(srouceData, certItem.Sign, alipayPublicKey, options.Charset, options.SignType);
+                                if (!jsonCheck)
+                                {
+                                    throw new AlipayException("cert check fail: check Cert and Data Fail JSON also");
+                                }
+                            }
+                            else
+                            {
+                                throw new AlipayException("cert check fail: check Cert and Data Fail!");
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    throw new AlipayException("cert check fail: check Cert and Data Fail! CertSN non-existent");
+                }
+            }
         }
 
         #endregion

+ 4 - 21
src/Essensoft.AspNetCore.Payment.Alipay/AlipayNotifyClient.cs

@@ -90,37 +90,20 @@ namespace Essensoft.AspNetCore.Payment.Alipay
                 throw new AlipayException("sign check fail: dictionary is Empty!");
             }
 
-            if (!dictionary.TryGetValue("sign", out var sign))
+            if (!dictionary.TryGetValue(AlipayConstants.SIGN, out var sign))
             {
                 throw new AlipayException("sign check fail: sign is Empty!");
             }
 
-            var prestr = GetSignContent(dictionary);
+            dictionary.Remove(AlipayConstants.SIGN);
+            dictionary.Remove(AlipayConstants.SIGN_TYPE);
+            var prestr = AlipaySignature.GetSignContent(dictionary);
             if (!AlipaySignature.RSACheckContent(prestr, sign, options.AlipayPublicKey, options.Charset, options.SignType))
             {
                 throw new AlipayException("sign check fail: check Sign Data Fail!");
             }
         }
 
-        private static string GetSignContent(IDictionary<string, string> dictionary)
-        {
-            if (dictionary == null || dictionary.Count == 0)
-            {
-                throw new ArgumentNullException(nameof(dictionary));
-            }
-
-            var sortPara = new SortedDictionary<string, string>(dictionary);
-            var sb = new StringBuilder();
-            foreach (var iter in sortPara)
-            {
-                if (!string.IsNullOrEmpty(iter.Value) && iter.Key != "sign" && iter.Key != "sign_type")
-                {
-                    sb.Append(iter.Key).Append("=").Append(iter.Value).Append("&");
-                }
-            }
-            return sb.Remove(sb.Length - 1, 1).ToString();
-        }
-
         #endregion
     }
 }

+ 17 - 0
src/Essensoft.AspNetCore.Payment.Alipay/AlipayPublicKeyManager.cs

@@ -0,0 +1,17 @@
+using System.Collections.Concurrent;
+
+namespace Essensoft.AspNetCore.Payment.Alipay
+{
+    public class AlipayPublicKeyManager
+    {
+        private readonly ConcurrentDictionary<string, string> _SNPublicKeyDictionary = new ConcurrentDictionary<string, string>();
+
+        public bool IsEmpty => _SNPublicKeyDictionary.IsEmpty;
+
+        public bool ContainsKey(string sn) => _SNPublicKeyDictionary.ContainsKey(sn);
+
+        public bool TryAdd(string sn, string publicKey) => _SNPublicKeyDictionary.TryAdd(sn, publicKey);
+
+        public bool TryGetValue(string sn, out string publicKey) => _SNPublicKeyDictionary.TryGetValue(sn, out publicKey);
+    }
+}

+ 2 - 0
src/Essensoft.AspNetCore.Payment.Alipay/Parser/AlipayJsonParser.cs

@@ -187,6 +187,7 @@ namespace Essensoft.AspNetCore.Payment.Alipay.Parser
                 Sign = GetSign(responseBody),
                 SignSourceDate = GetSignSourceData(request, responseBody)
             };
+
             return signItem;
         }
 
@@ -204,6 +205,7 @@ namespace Essensoft.AspNetCore.Payment.Alipay.Parser
                 CertSN = json[AlipayConstants.ALIPAY_CERT_SN]?.ToString(),
                 SignSourceDate = GetSignSourceData(request, responseBody)
             };
+
             return certItem;
         }
 

+ 0 - 8
src/Essensoft.AspNetCore.Payment.Alipay/Parser/SignItem.cs

@@ -1,17 +1,9 @@
 namespace Essensoft.AspNetCore.Payment.Alipay.Parser
 {
-    /// <summary>
-    /// 签名项目
-    /// </summary>
     public class SignItem
     {
         public string SignSourceDate { get; set; }
 
         public string Sign { get; set; }
-
-        public override string ToString()
-        {
-            return "{" + "Sign:" + Sign + ",SignSourceDate:" + SignSourceDate + "}";
-        }
     }
 }

+ 1 - 1
src/Essensoft.AspNetCore.Payment.Alipay/ServiceCollectionExtensions.cs

@@ -17,7 +17,7 @@ namespace Essensoft.AspNetCore.Payment.Alipay
         {
             services.AddHttpClient(nameof(AlipayClient));
 
-            services.AddSingleton<AlipayCertificateManager>();
+            services.AddSingleton<AlipayPublicKeyManager>();
             services.AddSingleton<IAlipayClient, AlipayClient>();
 
 #if NETCOREAPP3_1