From 9ae4c09a8256c60a925bea93eb08493209149ee6 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Thu, 25 Nov 2021 18:42:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(tenpayv3):=20=E6=96=B0=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=AF=86=E4=B8=AD=E8=AF=B7=E6=B1=82=E4=B8=AD=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=AD=97=E6=AE=B5=E7=9A=84=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WechatTenpayRequestEncryptionException.cs | 24 ++++++ ...TenpayClientEventVerificationExtensions.cs | 2 +- ...TenpayClientRequestEncryptionExtensions.cs | 82 +++++++++++++++++++ ...enpayClientResponseDecryptionExtensions.cs | 12 ++- ...eateApplyForSubMerchantApplymentRequest.cs | 12 +++ ...ifyApplyForSubMerchantSettlementRequest.cs | 1 + .../CreateApplyForSubjectApplymentRequest.cs | 5 ++ .../CreateBrandProfitSharingOrderRequest.cs | 1 + .../CreateEcommerceApplymentRequest.cs | 10 +++ ...reateEcommerceProfitSharingOrderRequest.cs | 1 + ...ddEcommerceProfitSharingReceiverRequest.cs | 1 + .../CreateProfitSharingOrderRequest.cs | 1 + .../AddProfitSharingReceiverRequest.cs | 3 +- .../AddProfitSharingReceiverResponse.cs | 3 +- .../SmartGuide/CreateSmartGuideRequest.cs | 2 + .../SmartGuide/QuerySmartGuidesRequest.cs | 1 + .../SmartGuide/UpdateSmartGuideRequest.cs | 2 + .../Batches/CreateTransferBatchRequest.cs | 2 + .../WechatTenpayClient.cs | 5 +- .../WechatTenpayRequestEncryptionTests.cs | 45 ++++++++++ 20 files changed, 202 insertions(+), 13 deletions(-) create mode 100644 src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Exceptions/WechatTenpayRequestEncryptionException.cs create mode 100644 src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientRequestEncryptionExtensions.cs create mode 100644 test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/WechatTenpayRequestEncryptionTests.cs diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Exceptions/WechatTenpayRequestEncryptionException.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Exceptions/WechatTenpayRequestEncryptionException.cs new file mode 100644 index 00000000..411f7f4e --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Exceptions/WechatTenpayRequestEncryptionException.cs @@ -0,0 +1,24 @@ +using System; + +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Exceptions +{ + public class WechatTenpayRequestEncryptionException : WechatTenpayException + { + /// + internal WechatTenpayRequestEncryptionException() + { + } + + /// + internal WechatTenpayRequestEncryptionException(string message) + : base(message) + { + } + + /// + internal WechatTenpayRequestEncryptionException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientEventVerificationExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientEventVerificationExtensions.cs index fcb06151..534b2eac 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientEventVerificationExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientEventVerificationExtensions.cs @@ -65,7 +65,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 { try { - var cert = client.CertificateManager.GetEntry(callbackSerialNumber)!; + var cert = client.CertificateManager.GetEntry(callbackSerialNumber); if (!cert.HasValue) { error = new Exceptions.WechatTenpayEventVerificationException("Verify signature of event failed, because there is no platform certificate matched the serial number."); diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientRequestEncryptionExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientRequestEncryptionExtensions.cs new file mode 100644 index 00000000..e52da044 --- /dev/null +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientRequestEncryptionExtensions.cs @@ -0,0 +1,82 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 +{ + /// + /// 为 提供请求模型敏感数据加密的扩展方法。 + /// + public static class WechatTenpayClientRequestEncryptionExtensions + { + /// + /// 加密请求中传入的敏感数据。该方法会改变传入的请求模型对象。 + /// + /// + /// + /// + public static TRequest EncryptRequestSensitiveProperty(this WechatTenpayClient client, TRequest request) + where TRequest : WechatTenpayRequest + { + if (client == null) throw new ArgumentNullException(nameof(client)); + if (request == null) throw new ArgumentNullException(nameof(request)); + + try + { + // 遍历并加密被标记为敏感数据的字段 + Utilities.ReflectionUtility.ReplacePropertyStringValue(ref request, (obj, prop, value) => + { + var attr = prop.GetCustomAttribute(); + if (attr == null) + return value; + + if (Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB.Equals(attr.Algorithm)) + { + if (client.CertificateManager == null) + throw new Exceptions.WechatTenpayRequestEncryptionException("Encrypt request failed, because there is no platform certificate in the manager."); + + string certificate; + + if (!string.IsNullOrEmpty(request.WechatpayCertSerialNumber)) + { + // 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值 + var cert = client.CertificateManager.GetEntry(request.WechatpayCertSerialNumber!); + if (!cert.HasValue) + { + throw new Exceptions.WechatTenpayEventVerificationException("Encrypt request failed, because there is no platform certificate matched the serial number."); + } + + certificate = cert.Value.Certificate; + } + else + { + // 如果未在请求中指定特定的平台证书序列号,从管理器中取过期时间最远的 + var certs = client.CertificateManager.AllEntries().OrderByDescending(e => e.ExpireTime); + if (!certs.Any()) + { + throw new Exceptions.WechatTenpayEventVerificationException("Encrypt request failed, because there is no platform certificate in the manager."); + } + + certificate = certs.First().Certificate; + } + + return Utilities.RSAUtility.EncryptWithECBByCertificate( + certificate: certificate, + plainText: value + ); + } + else + { + throw new Exceptions.WechatTenpayRequestEncryptionException("Unsupported encryption algorithm."); + } + }); + } + catch (Exception ex) when (!(ex is Exceptions.WechatTenpayRequestEncryptionException)) + { + throw new Exceptions.WechatTenpayRequestEncryptionException("Encrypt request failed. Please see the `InnerException` for more details.", ex); + } + + return request; + } + } +} diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientResponseDecryptionExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientResponseDecryptionExtensions.cs index 34cf3967..25b96692 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientResponseDecryptionExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Extensions/WechatTenpayClientResponseDecryptionExtensions.cs @@ -1,17 +1,15 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 { /// - /// 为 提供响应敏感数据解密的扩展方法。 + /// 为 提供响应模型敏感数据解密的扩展方法。 /// public static class WechatTenpayClientResponseDecryptionExtensions { /// - /// 解密响应中返回的敏感数据。该方法会改变传入的响应信息。 + /// 解密响应中返回的敏感数据。该方法会改变传入的响应模型对象。 /// /// /// @@ -22,9 +20,6 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 if (client == null) throw new ArgumentNullException(nameof(client)); if (response == null) throw new ArgumentNullException(nameof(response)); - if (string.IsNullOrEmpty(client.Credentials.MerchantCertPrivateKey)) - throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed, because there is no merchant private key."); - if (!response.IsSuccessful()) throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed, because the response is not successful."); @@ -40,6 +35,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 { if (Constants.EncryptionAlgorithms.AEAD_AES_256_GCM.Equals(certificateModel.EncryptCertificate?.Algorithm)) { + if (string.IsNullOrEmpty(client.Credentials.MerchantCertPrivateKey)) + throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed, because there is no merchant private key."); + certificateModel.EncryptCertificate.CipherText = Utilities.AESUtility.DecryptWithGCM( key: client.Credentials.MerchantV3Secret, iv: certificateModel.EncryptCertificate.Nonce, diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4SubMerchant/CreateApplyForSubMerchantApplymentRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4SubMerchant/CreateApplyForSubMerchantApplymentRequest.cs index 9e173dec..cdf673a5 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4SubMerchant/CreateApplyForSubMerchantApplymentRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4SubMerchant/CreateApplyForSubMerchantApplymentRequest.cs @@ -17,6 +17,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("contact_name")] [System.Text.Json.Serialization.JsonPropertyName("contact_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string Name { get; set; } = string.Empty; /// @@ -24,6 +25,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("mobile_phone")] [System.Text.Json.Serialization.JsonPropertyName("mobile_phone")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string Mobile { get; set; } = string.Empty; /// @@ -31,6 +33,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("contact_id_number")] [System.Text.Json.Serialization.JsonPropertyName("contact_id_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? IdCardNumber { get; set; } /// @@ -45,6 +48,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("contact_email")] [System.Text.Json.Serialization.JsonPropertyName("contact_email")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string Email { get; set; } = string.Empty; } @@ -198,6 +202,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_card_name")] [System.Text.Json.Serialization.JsonPropertyName("id_card_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdCardName { get; set; } = string.Empty; /// @@ -205,6 +210,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_card_number")] [System.Text.Json.Serialization.JsonPropertyName("id_card_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdCardNumber { get; set; } = string.Empty; /// @@ -236,6 +242,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_doc_name")] [System.Text.Json.Serialization.JsonPropertyName("id_doc_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdDocumentName { get; set; } = string.Empty; /// @@ -243,6 +250,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_doc_number")] [System.Text.Json.Serialization.JsonPropertyName("id_doc_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdDocumentNumber { get; set; } = string.Empty; /// @@ -325,6 +333,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("name")] [System.Text.Json.Serialization.JsonPropertyName("name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string Name { get; set; } = string.Empty; /// @@ -332,6 +341,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_number")] [System.Text.Json.Serialization.JsonPropertyName("id_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdNumber { get; set; } = string.Empty; /// @@ -708,6 +718,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("account_name")] [System.Text.Json.Serialization.JsonPropertyName("account_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string AccountName { get; set; } = string.Empty; /// @@ -715,6 +726,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("account_number")] [System.Text.Json.Serialization.JsonPropertyName("account_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string AccountNumber { get; set; } = string.Empty; /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4SubMerchant/Settlement/ModifyApplyForSubMerchantSettlementRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4SubMerchant/Settlement/ModifyApplyForSubMerchantSettlementRequest.cs index a20b32c7..f6cb7dab 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4SubMerchant/Settlement/ModifyApplyForSubMerchantSettlementRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4SubMerchant/Settlement/ModifyApplyForSubMerchantSettlementRequest.cs @@ -27,6 +27,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("account_number")] [System.Text.Json.Serialization.JsonPropertyName("account_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string AccountNumber { get; set; } = string.Empty; /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4Subject/CreateApplyForSubjectApplymentRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4Subject/CreateApplyForSubjectApplymentRequest.cs index 4ec44317..4598bcd7 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4Subject/CreateApplyForSubjectApplymentRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Apply4Subject/CreateApplyForSubjectApplymentRequest.cs @@ -17,6 +17,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("name")] [System.Text.Json.Serialization.JsonPropertyName("name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string Name { get; set; } = string.Empty; /// @@ -24,6 +25,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("mobile")] [System.Text.Json.Serialization.JsonPropertyName("mobile")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string Mobile { get; set; } = string.Empty; /// @@ -31,6 +33,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_card_number")] [System.Text.Json.Serialization.JsonPropertyName("id_card_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdCardNumber { get; set; } = string.Empty; } @@ -255,6 +258,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("identification_name")] [System.Text.Json.Serialization.JsonPropertyName("identification_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdentificationName { get; set; } = string.Empty; /// @@ -262,6 +266,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("identification_number")] [System.Text.Json.Serialization.JsonPropertyName("identification_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdentificationNumber { get; set; } = string.Empty; /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/BrandProfitSharing/CreateBrandProfitSharingOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/BrandProfitSharing/CreateBrandProfitSharingOrderRequest.cs index 6f5acc14..243c8cde 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/BrandProfitSharing/CreateBrandProfitSharingOrderRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/BrandProfitSharing/CreateBrandProfitSharingOrderRequest.cs @@ -31,6 +31,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("name")] [System.Text.Json.Serialization.JsonPropertyName("name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? Name { get; set; } /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceApplyments/CreateEcommerceApplymentRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceApplyments/CreateEcommerceApplymentRequest.cs index 52a77e23..0612381c 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceApplyments/CreateEcommerceApplymentRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceApplyments/CreateEcommerceApplymentRequest.cs @@ -99,6 +99,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_card_name")] [System.Text.Json.Serialization.JsonPropertyName("id_card_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdCardName { get; set; } = string.Empty; /// @@ -106,6 +107,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_card_number")] [System.Text.Json.Serialization.JsonPropertyName("id_card_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdCardNumber { get; set; } = string.Empty; /// @@ -130,6 +132,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_doc_name")] [System.Text.Json.Serialization.JsonPropertyName("id_doc_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdDocumentName { get; set; } = string.Empty; /// @@ -137,6 +140,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("id_doc_number")] [System.Text.Json.Serialization.JsonPropertyName("id_doc_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string IdDocumentNumber { get; set; } = string.Empty; /// @@ -161,6 +165,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("contact_name")] [System.Text.Json.Serialization.JsonPropertyName("contact_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string ContactName { get; set; } = string.Empty; /// @@ -168,6 +173,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("mobile_phone")] [System.Text.Json.Serialization.JsonPropertyName("mobile_phone")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string ContactMobile { get; set; } = string.Empty; /// @@ -175,6 +181,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("contact_id_card_number")] [System.Text.Json.Serialization.JsonPropertyName("contact_id_card_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string ContactIdCardNumber { get; set; } = string.Empty; /// @@ -182,6 +189,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("contact_email")] [System.Text.Json.Serialization.JsonPropertyName("contact_email")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string ContactEmail { get; set; } = string.Empty; } @@ -199,6 +207,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("account_name")] [System.Text.Json.Serialization.JsonPropertyName("account_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string AccountName { get; set; } = string.Empty; /// @@ -206,6 +215,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("account_number")] [System.Text.Json.Serialization.JsonPropertyName("account_number")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string AccountNumber { get; set; } = string.Empty; /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceProfitSharing/CreateEcommerceProfitSharingOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceProfitSharing/CreateEcommerceProfitSharingOrderRequest.cs index b376c9fd..64efc8c4 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceProfitSharing/CreateEcommerceProfitSharingOrderRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceProfitSharing/CreateEcommerceProfitSharingOrderRequest.cs @@ -31,6 +31,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("receiver_name")] [System.Text.Json.Serialization.JsonPropertyName("receiver_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? Name { get; set; } /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceProfitSharing/Receivers/AddEcommerceProfitSharingReceiverRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceProfitSharing/Receivers/AddEcommerceProfitSharingReceiverRequest.cs index d401d257..a0b6d11b 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceProfitSharing/Receivers/AddEcommerceProfitSharingReceiverRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/EcommerceProfitSharing/Receivers/AddEcommerceProfitSharingReceiverRequest.cs @@ -41,6 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("encrypted_name")] [System.Text.Json.Serialization.JsonPropertyName("encrypted_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? EncryptedName { get; set; } /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/CreateProfitSharingOrderRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/CreateProfitSharingOrderRequest.cs index 51b8a984..478c69d8 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/CreateProfitSharingOrderRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/CreateProfitSharingOrderRequest.cs @@ -31,6 +31,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("name")] [System.Text.Json.Serialization.JsonPropertyName("name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? Name { get; set; } /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/Receivers/AddProfitSharingReceiverRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/Receivers/AddProfitSharingReceiverRequest.cs index 473487ab..17ee9759 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/Receivers/AddProfitSharingReceiverRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/Receivers/AddProfitSharingReceiverRequest.cs @@ -41,10 +41,11 @@ public string Account { get; set; } = string.Empty; /// - /// 获取或设置接收方名称。 + /// 获取或设置接收方名称(需使用微信支付平台公钥加密)。 /// [Newtonsoft.Json.JsonProperty("name")] [System.Text.Json.Serialization.JsonPropertyName("name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? Name { get; set; } /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/Receivers/AddProfitSharingReceiverResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/Receivers/AddProfitSharingReceiverResponse.cs index ab70b54c..19c0b9fa 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/Receivers/AddProfitSharingReceiverResponse.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/ProfitSharing/Receivers/AddProfitSharingReceiverResponse.cs @@ -30,10 +30,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models public string Account { get; set; } = default!; /// - /// 获取或设置接收方名称。 + /// 获取或设置接收方名称(需使用商户私钥解密)。 /// [Newtonsoft.Json.JsonProperty("name")] [System.Text.Json.Serialization.JsonPropertyName("name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? Name { get; set; } /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/CreateSmartGuideRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/CreateSmartGuideRequest.cs index 5ee60a05..25697942 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/CreateSmartGuideRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/CreateSmartGuideRequest.cs @@ -41,6 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("name")] [System.Text.Json.Serialization.JsonPropertyName("name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string UserName { get; set; } = string.Empty; /// @@ -48,6 +49,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("mobile")] [System.Text.Json.Serialization.JsonPropertyName("mobile")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string UserMobile { get; set; } = string.Empty; /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/QuerySmartGuidesRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/QuerySmartGuidesRequest.cs index 6679d14d..3ef24199 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/QuerySmartGuidesRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/QuerySmartGuidesRequest.cs @@ -34,6 +34,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonIgnore] [System.Text.Json.Serialization.JsonIgnore] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? UserMobile { get; set; } /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/UpdateSmartGuideRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/UpdateSmartGuideRequest.cs index b6aba8f9..e8c0660b 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/UpdateSmartGuideRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/SmartGuide/UpdateSmartGuideRequest.cs @@ -27,6 +27,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("name")] [System.Text.Json.Serialization.JsonPropertyName("name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? UserName { get; set; } /// @@ -34,6 +35,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("mobile")] [System.Text.Json.Serialization.JsonPropertyName("mobile")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string? UserMobile { get; set; } /// diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Transfer/Batches/CreateTransferBatchRequest.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Transfer/Batches/CreateTransferBatchRequest.cs index 52be9883..a74cd929 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Transfer/Batches/CreateTransferBatchRequest.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/Models/Transfer/Batches/CreateTransferBatchRequest.cs @@ -45,6 +45,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("user_name")] [System.Text.Json.Serialization.JsonPropertyName("user_name")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string UserName { get; set; } = string.Empty; /// @@ -52,6 +53,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models /// [Newtonsoft.Json.JsonProperty("user_id_card")] [System.Text.Json.Serialization.JsonPropertyName("user_id_card")] + [WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)] public string UserIdCardNumber { get; set; } = string.Empty; } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/WechatTenpayClient.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/WechatTenpayClient.cs index 68399eba..0e04ff2b 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/WechatTenpayClient.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV3/WechatTenpayClient.cs @@ -87,10 +87,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3 flurlRequest.WithHeader("Wechatpay-Serial", request.WechatpayCertSerialNumber); } - if (AutoDecryptResponseSensitiveProperty) + if (AutoEncryptRequestSensitiveProperty) { - // this.EncryptRequestSensitiveProperty(request); - throw new NotImplementedException(); + this.EncryptRequestSensitiveProperty(request); } return flurlRequest; diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/WechatTenpayRequestEncryptionTests.cs b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/WechatTenpayRequestEncryptionTests.cs new file mode 100644 index 00000000..84601a16 --- /dev/null +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests/WechatTenpayRequestEncryptionTests.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests +{ + public class WechatTenpayRequestEncryptionTests + { + private const string RSA_CERTSN = "mock_sn"; + private const string RSA_CERTIFICATE = "-----BEGIN CERTIFICATE-----MIID9jCCAt6gAwIBAgIUFGJAVFxIU/1hoxpZC8Tc2xKFJHowDQYJKoZIhvcNAQELBQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsTFFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3QgQ0EwHhcNMjAwODAxMDczNTE5WhcNMjUwNzMxMDczNTE5WjCBhzETMBEGA1UEAwwKMTYwMTEwMzMxNDEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQLDCroo5XkurrvvIjkuIrmtbfvvInmlZnogrLnp5HmioDmnInpmZDlhazlj7gxCzAJBgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKq4IUw5Rq6iTUahjkr+aYvmn3Hg2tHNDyIru7ZdthXw27BCwnwwDyufq6SBwXypuf3wXKpcFggZo5dwjDR6XxlGXzNDKdW7JQkTxQXqABZZVCq4yMMkVns3xbmDfAI57bW0O0F40+NOqmU9gjqRwGNvE9bE8N3y2VyxYcz53rU4FCrlCOfsFeF1Z3usbWOK5IOYXjmdzr96xVWasO0URJ60GwDZyGxRdkzd5kJy6HnVVqOUbn3t7mKbGkzQq/j+D+pyMlhjtRV475ks5uex4gVWZgEqT1b9sx1pUQvVPr6/ZXtqTJwSg/YvSCx5RBiYCskAyA/5XH9t1p/WV1zFhXUCAwEAAaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAU44msdPGFg/r5JcWgUDEXWOqqCDiFNjWbhM/rO0A3TCV0yP5o7Se/yLsDizHGTUzZ2qg3bC02nn4RysEyMVQ+9tXsOtXQBHrmoZ5vS8ndqbE1YO33N6zxIUb0IN3yGZh3oVmQgTYYe1is4i5Sfiy7JdR6/uUrwQN13fzPaDCnNx6iVzrPSJJf1xiFdtpFFtK021prjMYG7csHiFQeelgyE8XtlQuLk0tsHBrJ2FHSdV4HISqONz0hMPL0xsZkBD/L/bvR3L0lqe8bsHOBCJOMyjxucI21mBRd7tc4AGiJlQt5jrUrbos5hul4/QUq7hFfZNDrEBMINbMyIpYWRbIqQ==-----END CERTIFICATE-----"; + private const string RSA_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCquCFMOUauok1GoY5K/mmL5p9x4NrRzQ8iK7u2XbYV8NuwQsJ8MA8rn6ukgcF8qbn98FyqXBYIGaOXcIw0el8ZRl8zQynVuyUJE8UF6gAWWVQquMjDJFZ7N8W5g3wCOe21tDtBeNPjTqplPYI6kcBjbxPWxPDd8tlcsWHM+d61OBQq5Qjn7BXhdWd7rG1jiuSDmF45nc6/esVVmrDtFESetBsA2chsUXZM3eZCcuh51VajlG597e5imxpM0Kv4/g/qcjJYY7UVeO+ZLObnseIFVmYBKk9W/bMdaVEL1T6+v2V7akycEoP2L0gseUQYmArJAMgP+Vx/bdaf1ldcxYV1AgMBAAECggEBAIwkgTkYX5ymIXeU0cFgXfZ5iHQsWJUXl4++hOaswPf78+wasZrOvPVbqsRtvA8BKWx7byZuV6uomHGN85p0xjJyYV4siWAps7pi3z7+m7m89OnpLO18m/2kiAzFEfyl3yxlWqtha9dSUXCwcIJx+ZPmsEuC+hPI8oQ0HQvuJtNtcDmaFBp57kxjlO4Xm0xnvq2waSmAF4cO6Dk62gksu8G7//IyIHXqmRWSpI2b/Yqf84XtwLN8Zvq/3otYuWc3pFOaKHg2sJ/JwSU+yGdeIOtEXXFIDSD6p3KvIvjrD47Da4optYReXP1k6e721z+XPBFP31i58iu1K8KOyjjJfsECgYEA41D6V1TrON31OOsM4jAtqyY/LVNdzT+I/S6lQruO9siRRJDYlAOdbM9VZnW/3rduPMBu4undbMkL5i8olnwmLeWSupClA2WmUc8DWHlBO2s0lE+ZDZgOtpZ9qOBfm7L6jbjvzbGoyJgV6SeIavH5SMQbFku5rlIi12hAb0Kv4uUCgYEAwELkPOKDH06SFOiuOzJcI4VyjK0V5LcWjceTPkaT9d1evkrNgrHiG8CjYQ3V0lqSKhckFg2PwmPk72rC4n3aFCXurimv96rG5sBFFLgPbyoKZ3Z9JoDoe/6u6YIAV/GzxJ5QJaWQQ59MxToILYyq6aA6SrNOqRUPsKuvN8jBH1ECgYEA3dJ6yNgcRj0KfIWa5+qd1iMXiZKNuamjc3WeXTWL+DSW1bMHNcElUTYuHzMOjjavw2cBjjsrEWpLS09/qwHxe95IRfi6nksGd1Ss7hw9VM9z2rqmH4bf7LuEWlTB171bFQuAL1iL3VvUHdavH7WLTr/XsvUod/y89TlNj4UjACUCgYBBR3UPZyl2O8tF5isiVlsKhIj8UtiYK8IwqY7JGlWqqVs96VAWDCflnGbc0UHEhpQSToEmK7ygGCLnV6yMEoc1SBvebrEcupOGTcom2sgCypd1wbmElUhasYLaLhXHxn1vSQGVhr2Q+EmsvaOBM73kTU79hhwzNL97ERARNMy9wQKBgHCogsDPxSh8hEJR/PBom6v/3R+Ou86K8nzulbyHGkDM7I2R//zGx4en1VxWFhsqywsGedugv1BMkgEcCFkTjIXfYh9Uwn5iGUAZCczR5ZoETYAAzK2a/uaWf92iZEU0bHJaBA4egNxk1bFe+ECFTteN8Ag+UVtx0HU6ZNlyfetH-----END PRIVATE KEY-----"; + private const string MockText = "mock_text"; + private readonly Lazy MockClientInstance = new Lazy(() => + { + var certManager = new Settings.InMemoryCertificateManager(); + certManager.AddEntry(new Settings.CertificateEntry( + serialNumber: RSA_CERTSN, + certificate: RSA_CERTIFICATE, + effectiveTime: DateTimeOffset.MinValue, + expireTime: DateTimeOffset.MaxValue + )); + return new WechatTenpayClient(new WechatTenpayClientOptions() + { + CertificateManager = certManager + }); + }, isThreadSafe: false); + + [Fact(DisplayName = "加密请求中的敏感数据([POST] /profitsharing/receivers/add)")] + public void DecryptResponseSensitiveProperty_AddProfitSharingReceiverRequest() + { + var mock = new Models.AddProfitSharingReceiverRequest() + { + Account = MockText, + Name = MockText + }; + var data = MockClientInstance.Value.EncryptRequestSensitiveProperty(mock); + + Assert.Equal(MockText, data.Account); + Assert.Equal(MockText, Utilities.RSAUtility.DecryptWithECB(RSA_PRIVATE_KEY, data.Name)); + } + } +}