mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2025-07-15 14:04:32 +08:00
feat(tenpayv3): 废弃原有的解密响应中加密字段的相关扩展方法,重新基于反射和特性实现
This commit is contained in:
parent
bfa6557314
commit
906c667117
@ -49,7 +49,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Sample_Net5.Services.BackgroundSe
|
||||
var response = await client.ExecuteQueryCertificatesAsync(request, cancellationToken: stoppingToken);
|
||||
if (response.IsSuccessful())
|
||||
{
|
||||
client.DecryptResponseEncryptedData(ref response);
|
||||
response = client.DecryptResponseSensitiveProperty(response);
|
||||
foreach (var certificateModel in response.CertificateList)
|
||||
{
|
||||
_certificateManager.AddEntry(new CertificateEntry(certificateModel));
|
||||
|
3
src/SKIT.FlurlHttpClient.Wechat.TenpayV3/AssemblyInfo.cs
Normal file
3
src/SKIT.FlurlHttpClient.Wechat.TenpayV3/AssemblyInfo.cs
Normal file
@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests")]
|
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class WechatTenpaySensitivePropertyAttribute : Attribute
|
||||
{
|
||||
public string Algorithm { get; }
|
||||
|
||||
public WechatTenpaySensitivePropertyAttribute(string algorithm)
|
||||
{
|
||||
Algorithm = algorithm;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants
|
||||
{
|
||||
public static class EncryptionAlgorithms
|
||||
{
|
||||
public const string AEAD_AES_256_GCM = "AEAD_AES_256_GCM";
|
||||
|
||||
public const string RSA_2048_PKCS8_ECB = "RSA_2048_PKCS18_ECB";
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants
|
||||
{
|
||||
internal static class FormDataFields
|
||||
{
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants
|
||||
{
|
||||
public static class SignAlgorithms
|
||||
{
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants
|
||||
{
|
||||
public static class SignTypes
|
||||
{
|
||||
|
@ -70,7 +70,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exceptions.WechatTenpayEventDecryptionException("Unknown encrypt algorithm of the resource.");
|
||||
throw new Exceptions.WechatTenpayEventDecryptionException("Unsupported encrypt algorithm of the resource.");
|
||||
}
|
||||
|
||||
return client.JsonSerializer.Deserialize<T>(plainJson);
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
{
|
||||
@ -15,7 +16,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.GetEcommerceApplymentByOutRequestNumberResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.GetEcommerceApplymentByOutRequestNumberResponse response)
|
||||
public static TResponse DecryptResponseSensitiveProperty<TResponse>(this WechatTenpayClient client, TResponse response)
|
||||
where TResponse : WechatTenpayResponse
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (response == null) throw new ArgumentNullException(nameof(response));
|
||||
@ -26,159 +28,17 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
if (!response.IsSuccessful())
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed, because the response is not successful.");
|
||||
|
||||
if (response.AccountValidation != null)
|
||||
try
|
||||
{
|
||||
IList<Exception> exceptions = new List<Exception>();
|
||||
|
||||
var accountValidationModel = response.AccountValidation;
|
||||
|
||||
if (!string.IsNullOrEmpty(accountValidationModel.AccountName))
|
||||
// [GET] /certificates 接口的响应模型需特殊处理
|
||||
if (response is Models.QueryCertificatesResponse queryCertificatesResponse)
|
||||
{
|
||||
try
|
||||
if (queryCertificatesResponse.CertificateList == null)
|
||||
return response;
|
||||
|
||||
foreach (var certificateModel in queryCertificatesResponse.CertificateList)
|
||||
{
|
||||
accountValidationModel.AccountName = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
accountValidationModel.AccountName
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(accountValidationModel.AccountNumber))
|
||||
{
|
||||
try
|
||||
{
|
||||
accountValidationModel.AccountNumber = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
accountValidationModel.AccountNumber!
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Any())
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", new AggregateException(exceptions));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.GetEcommerceBillFundflowBillResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.GetEcommerceBillFundflowBillResponse response)
|
||||
{
|
||||
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.");
|
||||
|
||||
if (response.DownloadBillList != null)
|
||||
{
|
||||
IList<Exception> exceptions = new List<Exception>();
|
||||
|
||||
foreach (var downloadBillModel in response.DownloadBillList)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(downloadBillModel.EncryptKey))
|
||||
{
|
||||
try
|
||||
{
|
||||
downloadBillModel.EncryptKey = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
downloadBillModel.EncryptKey
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Any())
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", new AggregateException(exceptions));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.GetMerchantServiceComplaintByComplaintIdResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.GetMerchantServiceComplaintByComplaintIdResponse response)
|
||||
{
|
||||
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.");
|
||||
|
||||
if (response.ComplaintDetail != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(response.PayerPhone))
|
||||
{
|
||||
try
|
||||
{
|
||||
response.PayerPhone = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
response.PayerPhone!
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.QueryCertificatesResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.QueryCertificatesResponse response)
|
||||
{
|
||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
||||
if (response == null) throw new ArgumentNullException(nameof(response));
|
||||
|
||||
if (string.IsNullOrEmpty(client.Credentials.MerchantV3Secret))
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed, because there is no merchant secret.");
|
||||
|
||||
if (!response.IsSuccessful())
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed, because the response is not successful.");
|
||||
|
||||
if (response.CertificateList != null)
|
||||
{
|
||||
IList<Exception> exceptions = new List<Exception>();
|
||||
|
||||
foreach (var certificateModel in response.CertificateList)
|
||||
{
|
||||
if (Constants.EncryptionAlgorithms.AEAD_AES_256_GCM.Equals(certificateModel.EncryptCertificate?.Algorithm))
|
||||
{
|
||||
try
|
||||
if (Constants.EncryptionAlgorithms.AEAD_AES_256_GCM.Equals(certificateModel.EncryptCertificate?.Algorithm))
|
||||
{
|
||||
certificateModel.EncryptCertificate.CipherText = Utilities.AESUtility.DecryptWithGCM(
|
||||
key: client.Credentials.MerchantV3Secret,
|
||||
@ -187,266 +47,38 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
cipherText: certificateModel.EncryptCertificate.CipherText
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Unsupported decryption algorithm.");
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// 遍历并解密被标记为敏感数据的字段
|
||||
Utilities.ReflectionUtility.ReplacePropertyStringValue(ref response, (obj, prop, value) =>
|
||||
{
|
||||
var attr = prop.GetCustomAttribute<WechatTenpaySensitivePropertyAttribute>();
|
||||
if (attr == null)
|
||||
return value;
|
||||
|
||||
if (Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB.Equals(attr.Algorithm))
|
||||
{
|
||||
return Utilities.RSAUtility.DecryptWithECB(
|
||||
privateKey: client.Credentials.MerchantCertPrivateKey,
|
||||
cipherText: value
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
exceptions.Add(new Exception("Unknown encrypt algorithm of the certificate."));
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Unsupported decryption algorithm.");
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Any())
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", new AggregateException(exceptions));
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.QueryMerchantServiceComplaintsResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.QueryMerchantServiceComplaintsResponse response)
|
||||
{
|
||||
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.");
|
||||
|
||||
if (response.ComplaintList != null)
|
||||
catch (Exception ex) when (!(ex is Exceptions.WechatTenpayResponseDecryptionException))
|
||||
{
|
||||
IList<Exception> exceptions = new List<Exception>();
|
||||
|
||||
foreach (var complaintModel in response.ComplaintList)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(complaintModel.PayerPhone))
|
||||
{
|
||||
try
|
||||
{
|
||||
complaintModel.PayerPhone = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
complaintModel.PayerPhone!
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Any())
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", new AggregateException(exceptions));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.QuerySmartGuidesResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.QuerySmartGuidesResponse response)
|
||||
{
|
||||
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.");
|
||||
|
||||
if (response.GuideList != null)
|
||||
{
|
||||
IList<Exception> exceptions = new List<Exception>();
|
||||
|
||||
foreach (var guideModel in response.GuideList)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(guideModel.UserName))
|
||||
{
|
||||
try
|
||||
{
|
||||
guideModel.UserName = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
guideModel.UserName
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(guideModel.UserMobile))
|
||||
{
|
||||
try
|
||||
{
|
||||
guideModel.UserMobile = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
guideModel.UserMobile
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Any())
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", new AggregateException(exceptions));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.GetTransferBatchDetailByOutDetailNumberResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.GetTransferBatchDetailByOutDetailNumberResponse response)
|
||||
{
|
||||
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.");
|
||||
|
||||
if (!string.IsNullOrEmpty(response.UserName))
|
||||
{
|
||||
try
|
||||
{
|
||||
response.UserName = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
response.UserName
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.GetTransferBatchDetailByDetailIdResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.GetTransferBatchDetailByDetailIdResponse response)
|
||||
{
|
||||
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.");
|
||||
|
||||
if (!string.IsNullOrEmpty(response.UserName))
|
||||
{
|
||||
try
|
||||
{
|
||||
response.UserName = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
response.UserName
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.GetPartnerTransferBatchDetailByOutDetailNumberResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.GetPartnerTransferBatchDetailByOutDetailNumberResponse response)
|
||||
{
|
||||
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.");
|
||||
|
||||
if (!string.IsNullOrEmpty(response.UserName))
|
||||
{
|
||||
try
|
||||
{
|
||||
response.UserName = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
response.UserName
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>解密响应中返回的敏感数据。该方法会改变传入的响应信息。</para>
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="response"></param>
|
||||
/// <returns></returns>
|
||||
public static Models.GetPartnerTransferBatchDetailByDetailIdResponse DecryptResponseEncryptedData(this WechatTenpayClient client, ref Models.GetPartnerTransferBatchDetailByDetailIdResponse response)
|
||||
{
|
||||
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.");
|
||||
|
||||
if (!string.IsNullOrEmpty(response.UserName))
|
||||
{
|
||||
try
|
||||
{
|
||||
response.UserName = Utilities.RSAUtility.DecryptWithECB(
|
||||
client.Credentials.MerchantCertPrivateKey,
|
||||
response.UserName
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed.", ex);
|
||||
}
|
||||
throw new Exceptions.WechatTenpayResponseDecryptionException("Decrypt response failed. Please see the `InnerException` for more details.", ex);
|
||||
}
|
||||
|
||||
return response;
|
||||
|
@ -45,6 +45,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("encrypt_key")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("encrypt_key")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public string EncryptKey { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
|
@ -42,6 +42,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("ciphertext")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("ciphertext")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.AEAD_AES_256_GCM)]
|
||||
public string CipherText { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[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; } = default!;
|
||||
|
||||
/// <summary>
|
||||
@ -24,6 +25,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("account_no")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("account_no")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public string? AccountNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -45,6 +45,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("encrypt_key")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("encrypt_key")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public string EncryptKey { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
|
@ -94,6 +94,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("payer_phone")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("payer_phone")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public string? PayerPhone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -58,6 +58,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("payer_phone")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("payer_phone")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public string? PayerPhone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -20,6 +20,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("username")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("username")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public override string UserName { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("encrypt_key")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("encrypt_key")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public string EncryptKey { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
|
@ -38,6 +38,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("name")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("name")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public string UserName { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
@ -45,6 +46,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("mobile")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("mobile")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public string UserMobile { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
|
@ -83,6 +83,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("user_name")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("user_name")]
|
||||
[WechatTenpaySensitiveProperty(algorithm: Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB)]
|
||||
public virtual string UserName { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings
|
||||
{
|
||||
@ -49,7 +50,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings
|
||||
|
||||
public override IEnumerable<CertificateEntry> AllEntries()
|
||||
{
|
||||
return _dict.Values;
|
||||
return _dict.Values.Where(e => e.IsAvailable()).ToArray();
|
||||
}
|
||||
|
||||
public override void AddEntry(CertificateEntry entry)
|
||||
|
@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
|
||||
{
|
||||
internal static class ReflectionUtility
|
||||
{
|
||||
public delegate string ReplacePropertyStringValueReplacement(object obj, PropertyInfo prop, string value);
|
||||
|
||||
public static void ReplacePropertyStringValue<T>(ref T obj, ReplacePropertyStringValueReplacement replacement)
|
||||
{
|
||||
InnerReplacePropertyStringValue(ref obj, replacement, null);
|
||||
}
|
||||
|
||||
private static void InnerReplacePropertyStringValue<T>(ref T obj, ReplacePropertyStringValueReplacement replacement, PropertyInfo? currentProp)
|
||||
{
|
||||
if (obj == null) throw new ArgumentNullException(nameof(obj));
|
||||
if (replacement == null) throw new ArgumentNullException(nameof(replacement));
|
||||
|
||||
Type objType = obj.GetType();
|
||||
if (!objType.IsClass)
|
||||
throw new NotSupportedException();
|
||||
|
||||
if (objType.IsArray)
|
||||
{
|
||||
var array = (obj as Array)!;
|
||||
for (int i = 0, len = array.Length; i < len; i++)
|
||||
{
|
||||
object? element = array.GetValue(i);
|
||||
if (element is null)
|
||||
continue;
|
||||
|
||||
Type elementType = element.GetType();
|
||||
if (elementType == typeof(string))
|
||||
{
|
||||
if (currentProp == null)
|
||||
continue;
|
||||
|
||||
string oldValue = (string)element!;
|
||||
string newValue = replacement(obj, currentProp, oldValue);
|
||||
array.SetValue(newValue, i);
|
||||
}
|
||||
else if (elementType.IsClass)
|
||||
{
|
||||
InnerReplacePropertyStringValue(ref element, replacement, currentProp);
|
||||
array.SetValue(element, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (obj is IList list)
|
||||
{
|
||||
for (int i = 0, len = list.Count; i < len; i++)
|
||||
{
|
||||
object? element = list[i];
|
||||
if (element is null)
|
||||
continue;
|
||||
|
||||
Type elementType = element.GetType();
|
||||
if (elementType == typeof(string))
|
||||
{
|
||||
if (currentProp == null)
|
||||
continue;
|
||||
|
||||
string oldValue = (string)element!;
|
||||
string newValue = replacement(obj, currentProp, oldValue);
|
||||
list[i] = newValue;
|
||||
}
|
||||
else if (elementType.IsClass)
|
||||
{
|
||||
InnerReplacePropertyStringValue(ref element, replacement, currentProp);
|
||||
list[i] = element;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (obj is IDictionary dict)
|
||||
{
|
||||
foreach (DictionaryEntry entry in dict)
|
||||
{
|
||||
object? entryValue = entry.Value;
|
||||
if (entryValue is null)
|
||||
continue;
|
||||
|
||||
Type entryValueType = entryValue.GetType();
|
||||
if (entryValueType == typeof(string))
|
||||
{
|
||||
if (currentProp == null)
|
||||
continue;
|
||||
|
||||
string oldValue = (string)entryValue!;
|
||||
string newValue = replacement(obj, currentProp, oldValue);
|
||||
dict[entry.Key] = newValue;
|
||||
}
|
||||
else if (entryValueType.IsClass)
|
||||
{
|
||||
InnerReplacePropertyStringValue(ref entryValue, replacement, currentProp);
|
||||
dict[entry.Key] = entryValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var childProp in objType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (!childProp.CanWrite)
|
||||
continue;
|
||||
|
||||
Type propType = childProp.PropertyType;
|
||||
if (propType == typeof(string))
|
||||
{
|
||||
string oldValue = (string)childProp.GetValue(obj, null)!;
|
||||
string newValue = replacement(obj, childProp, oldValue);
|
||||
childProp.SetValue(obj, newValue);
|
||||
}
|
||||
else if (propType.IsClass)
|
||||
{
|
||||
object? value = childProp.GetValue(obj, null);
|
||||
if (value is null)
|
||||
continue;
|
||||
|
||||
InnerReplacePropertyStringValue(ref value, replacement, childProp);
|
||||
childProp.SetValue(obj, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,17 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
/// <summary>
|
||||
/// 获取当前客户端使用的微信商户平台证书管理器。
|
||||
/// </summary>
|
||||
internal Settings.CertificateManager CertificateManager { get; }
|
||||
public Settings.CertificateManager CertificateManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否自动加密请求中的敏感字段数据。
|
||||
/// </summary>
|
||||
protected bool AutoEncryptRequestSensitiveProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否自动解密请求中的敏感字段数据。
|
||||
/// </summary>
|
||||
protected bool AutoDecryptResponseSensitiveProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 用指定的配置项初始化 <see cref="WechatTenpayClient"/> 类的新实例。
|
||||
@ -35,6 +45,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
|
||||
Credentials = new Settings.Credentials(options);
|
||||
CertificateManager = options.CertificateManager;
|
||||
AutoEncryptRequestSensitiveProperty = options.AutoEncryptRequestSensitiveProperty;
|
||||
AutoDecryptResponseSensitiveProperty = options.AutoDecryptResponseSensitiveProperty;
|
||||
|
||||
FlurlClient.BaseUrl = options.Endpoints ?? WechatTenpayEndpoints.DEFAULT;
|
||||
FlurlClient.Headers.Remove("Accept");
|
||||
@ -75,6 +87,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
flurlRequest.WithHeader("Wechatpay-Serial", request.WechatpayCertSerialNumber);
|
||||
}
|
||||
|
||||
if (AutoDecryptResponseSensitiveProperty)
|
||||
{
|
||||
// this.EncryptRequestSensitiveProperty(request);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return flurlRequest;
|
||||
}
|
||||
|
||||
@ -152,6 +170,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
result.WechatpayTimestamp = flurlResponse.Headers.GetAll("Wechatpay-Timestamp").FirstOrDefault() ?? string.Empty;
|
||||
result.WechatpaySignature = flurlResponse.Headers.GetAll("Wechatpay-Signature").FirstOrDefault() ?? string.Empty;
|
||||
result.WechatpayCertSerialNumber = flurlResponse.Headers.GetAll("Wechatpay-Serial").FirstOrDefault() ?? string.Empty;
|
||||
|
||||
if (AutoDecryptResponseSensitiveProperty)
|
||||
{
|
||||
this.DecryptResponseSensitiveProperty(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,16 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
|
||||
/// </summary>
|
||||
public string MerchantCertPrivateKey { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置是否自动加密请求中的敏感字段数据。
|
||||
/// </summary>
|
||||
public bool AutoEncryptRequestSensitiveProperty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置是否自动解密请求中的敏感字段数据。
|
||||
/// </summary>
|
||||
public bool AutoDecryptResponseSensitiveProperty { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置微信商户平台证书管理器。
|
||||
/// <para>默认值:<see cref="Settings.InMemoryCertificateManager"/></para>
|
||||
|
@ -0,0 +1,189 @@
|
||||
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 WechatTenpayResponseDecryptionTests
|
||||
{
|
||||
private const string RSA_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCd094X2jr6wrL1pIBTSbKX5hVqxyCJhKLqaf31KQsw2LdzfXEOL45KiYr6IshwZaSPFok7Sq+1wLrO6/DZ7/GSmzVwdFIV/zjDbI3jaSHt69ZAJGBS212AzC4cwHvpGrHm3kqzVifJyUNxsoTvWklRGSE50pPWaXoxmdPgD5da8IPDHNzkPM0QYTOlHmIRsDQZpWiZRPaFdrN3i/qG9toCRCPVptCVNYrWLDEFXh3ioOqOdTk1fHxFczaNf4YvQhsqXlG/lGiqGbuPuFYfF/RvS9KfptAM1yycknTslkSeiOpA22sFbDdb+m7DHv+nc8DQPPmD7SxDwVIDTcIS7pEJAgMBAAECggEAM6E+czEZwDR3FLKGpDhkqxpCgVa4xyPTo7mesVeG6KjMgoRTup9F/g42n5NHXLVzereVlwbcqiltMwmuFa1PqnUJq0ryG24NZKibVfxrdAiFYyBDPneyg9LHdvJk1qG79tlbOIWDqJglbwlGQYKYn2YIH4FKiYahyZ4X2KFhEwc9mWydHSOyN8zOOJcRCy1FzUcUvITRKob+Q9epz9/4/VX0g1AnB6FwIR5pBqwMYLSv+g+JxfVKPRnSaIxq/2HOvhiyJ7fUqX1yGI+konJJHrn66JIux8xt4SxEnomZBveHlOMUTgTqovxpXisbvXQGBDV7dwW/qhGZet6h57qogQKBgQDNFLP9S6aPnwseeavxK0ygQpgJRbXbHCyqH/mVA/Pg9DaIhxl7+JCC1lS/JuZslow4t3JvjwixAzQEz9SkwUuiRO5vUWb5R+DZJ8HeqtMfoll6wxepu1TQT1pTPnSHliJQP15k5AQ1bkTZjcGA79iUmkksa4EP/GWtOooE8JhMEQKBgQDFA6oyBtbMyWlnGmdsieQuRsjCklZhPL93INX5VUfcfRqQdhqrmoaJg+OZTwVrARp6VHGEaURBTSj6bSoRbBckFNxjVsL6Utpgof+ZWmr5u8ZGHGHIPJMLt7GxI9SItpNNNY33OiUkkfSH4zHK5KZeG9sKKraQwITJCwLZUnnNeQKBgChHkLKHUUeULVLnAuZzYrF3YvUvQ/CtL/iaHyMti5D7Zlqabl7zCy8nea2xrkBVsWTSYx+WMFbUEjt/tnxFmt1cPJiQnHEJtRfxvxpE4wKrmHeMKfGkYZwoec0vzyNyUXsBd0DJqCn2Zn90YDU65ocJZqXa15aUNEQ54zHlL4SBAoGAHbve3OwBUSj4unHWuB/bi0xtkkgJt2U2tGEFSjsfvFw5PSJGBi4tLeX03Ld7ZtnkyB+kfkpw3bYqgBknpzd8CpsHZAq9JJCKmtj4PYnS6Vv4oa4458KUoskXjVeOBRAhDR8PDQf+gRVyJWwZoLh/j2Z+2Xr20MPthnYd+PSko2kCgYBra4rMhYx2Hg0rRe2O7ju+MPm+JK01VpbvwDTnEPnYgMImDmLAXF6GljCt3iy/8X1WcjMPxGjTJ/xfTMne/aqKwvPhZCBL4DdNLNRzppCovsaaMHzrQzy4cvg0IEhIprFeR7ED4eMs8zLUhl3vgNhHOkeQ7cyuEnTl5wB9xOkbSw==-----END PRIVATE KEY-----";
|
||||
private const string RSA_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAndPeF9o6+sKy9aSAU0myl+YVascgiYSi6mn99SkLMNi3c31xDi+OSomK+iLIcGWkjxaJO0qvtcC6zuvw2e/xkps1cHRSFf84w2yN42kh7evWQCRgUttdgMwuHMB76Rqx5t5Ks1YnyclDcbKE71pJURkhOdKT1ml6MZnT4A+XWvCDwxzc5DzNEGEzpR5iEbA0GaVomUT2hXazd4v6hvbaAkQj1abQlTWK1iwxBV4d4qDqjnU5NXx8RXM2jX+GL0IbKl5Rv5Roqhm7j7hWHxf0b0vSn6bQDNcsnJJ07JZEnojqQNtrBWw3W/puwx7/p3PA0Dz5g+0sQ8FSA03CEu6RCQIDAQAB-----END PUBLIC KEY-----";
|
||||
private const string MockText = "mock_text";
|
||||
private readonly Lazy<WechatTenpayClient> MockClientInstance = new Lazy<WechatTenpayClient>(() =>
|
||||
{
|
||||
return new WechatTenpayClient(new WechatTenpayClientOptions()
|
||||
{
|
||||
MerchantCertPrivateKey = RSA_PRIVATE_KEY
|
||||
});
|
||||
}, isThreadSafe: false);
|
||||
|
||||
[Fact(DisplayName = "解密响应中的敏感数据([GET] /ecommerce/applyments/out-request-no/{out_request_no})")]
|
||||
public void DecryptResponseSensitiveProperty_GetEcommerceApplymentByOutRequestNumberResponseTest()
|
||||
{
|
||||
var mock = new Models.GetEcommerceApplymentByOutRequestNumberResponse()
|
||||
{
|
||||
RawStatus = 200,
|
||||
ApplymentState = MockText,
|
||||
AccountValidation = new Models.GetEcommerceApplymentByOutRequestNumberResponse.Types.AccountValidation()
|
||||
{
|
||||
AccountName = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText),
|
||||
AccountNumber = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText),
|
||||
DestinationAccountName = MockText,
|
||||
DestinationAccountNumber = MockText
|
||||
}
|
||||
};
|
||||
var data = MockClientInstance.Value.DecryptResponseSensitiveProperty(mock);
|
||||
|
||||
Assert.Equal(MockText, data.ApplymentState);
|
||||
Assert.Equal(MockText, data.AccountValidation.AccountNumber);
|
||||
Assert.Equal(MockText, data.AccountValidation.DestinationAccountName);
|
||||
Assert.Equal(MockText, data.AccountValidation.DestinationAccountNumber);
|
||||
Assert.Equal(MockText, data.AccountValidation.AccountName);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "解密响应中的敏感数据([GET] /ecommerce/bill/fundflowbill)")]
|
||||
public void DecryptResponseSensitiveProperty_GetEcommerceBillFundflowBillResponse()
|
||||
{
|
||||
var mock = new Models.GetEcommerceBillFundflowBillResponse()
|
||||
{
|
||||
RawStatus = 200,
|
||||
DownloadBillCount = 1,
|
||||
DownloadBillList = new Models.GetEcommerceBillFundflowBillResponse.Types.DownloadBill[]
|
||||
{
|
||||
new Models.GetEcommerceBillFundflowBillResponse.Types.DownloadBill()
|
||||
{
|
||||
DownloadUrl = MockText,
|
||||
EncryptKey = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText)
|
||||
},
|
||||
new Models.GetEcommerceBillFundflowBillResponse.Types.DownloadBill()
|
||||
{
|
||||
DownloadUrl = MockText,
|
||||
EncryptKey = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText)
|
||||
}
|
||||
}
|
||||
};
|
||||
var data = MockClientInstance.Value.DecryptResponseSensitiveProperty(mock);
|
||||
|
||||
Assert.Equal(1, data.DownloadBillCount);
|
||||
Assert.Equal(MockText, data.DownloadBillList[0].DownloadUrl);
|
||||
Assert.Equal(MockText, data.DownloadBillList[0].EncryptKey);
|
||||
Assert.Equal(MockText, data.DownloadBillList[1].DownloadUrl);
|
||||
Assert.Equal(MockText, data.DownloadBillList[1].EncryptKey);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "解密响应中的敏感数据([GET] /merchant-service/complaints-v2/{complaint_id})")]
|
||||
public void DecryptResponseSensitiveProperty_GetMerchantServiceComplaintByComplaintIdResponse()
|
||||
{
|
||||
var mock = new Models.GetMerchantServiceComplaintByComplaintIdResponse()
|
||||
{
|
||||
RawStatus = 200,
|
||||
ComplaintOrderList = new Models.GetMerchantServiceComplaintByComplaintIdResponse.Types.ComplaintOrder[]
|
||||
{
|
||||
new Models.GetMerchantServiceComplaintByComplaintIdResponse.Types.ComplaintOrder()
|
||||
{
|
||||
TransactionId = MockText
|
||||
}
|
||||
},
|
||||
ComplaintDetail = MockText,
|
||||
PayerPhone = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText)
|
||||
};
|
||||
var data = MockClientInstance.Value.DecryptResponseSensitiveProperty(mock);
|
||||
|
||||
Assert.Equal(MockText, data.ComplaintDetail);
|
||||
Assert.Equal(MockText, data.PayerPhone);
|
||||
Assert.Equal(MockText, data.ComplaintOrderList[0].TransactionId);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "解密响应中的敏感数据([GET] /merchant-service/complaints-v2)")]
|
||||
public void DecryptResponseSensitiveProperty_QueryMerchantServiceComplaintsResponse()
|
||||
{
|
||||
var mock = new Models.QueryMerchantServiceComplaintsResponse()
|
||||
{
|
||||
RawStatus = 200,
|
||||
ComplaintList = new Models.QueryMerchantServiceComplaintsResponse.Types.Complaint[]
|
||||
{
|
||||
new Models.QueryMerchantServiceComplaintsResponse.Types.Complaint()
|
||||
{
|
||||
PayerPhone = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText),
|
||||
PayerOpenId = MockText
|
||||
}
|
||||
}
|
||||
};
|
||||
var data = MockClientInstance.Value.DecryptResponseSensitiveProperty(mock);
|
||||
|
||||
Assert.Equal(MockText, data.ComplaintList[0].PayerPhone);
|
||||
Assert.Equal(MockText, data.ComplaintList[0].PayerOpenId);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "解密响应中的敏感数据([GET] /smartguide/guides)")]
|
||||
public void DecryptResponseSensitiveProperty_QuerySmartGuidesResponse()
|
||||
{
|
||||
var mock = new Models.QuerySmartGuidesResponse()
|
||||
{
|
||||
RawStatus = 200,
|
||||
GuideList = new Models.QuerySmartGuidesResponse.Types.Guide[]
|
||||
{
|
||||
new Models.QuerySmartGuidesResponse.Types.Guide()
|
||||
{
|
||||
GuideId = MockText,
|
||||
UserName = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText),
|
||||
UserMobile = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText)
|
||||
}
|
||||
}
|
||||
};
|
||||
var data = MockClientInstance.Value.DecryptResponseSensitiveProperty(mock);
|
||||
|
||||
Assert.Equal(MockText, data.GuideList[0].GuideId);
|
||||
Assert.Equal(MockText, data.GuideList[0].UserName);
|
||||
Assert.Equal(MockText, data.GuideList[0].UserMobile);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "解密响应中的敏感数据([GET] /transfer/batches/out-batch-no/{out_batch_no}/details/out-detail-no/{out_detail_no})")]
|
||||
public void DecryptResponseSensitiveProperty_GetTransferBatchDetailByOutDetailNumberResponse()
|
||||
{
|
||||
var mock = new Models.GetTransferBatchDetailByOutDetailNumberResponse()
|
||||
{
|
||||
RawStatus = 200,
|
||||
UserName = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText),
|
||||
OpenId = MockText
|
||||
};
|
||||
var data = MockClientInstance.Value.DecryptResponseSensitiveProperty(mock);
|
||||
|
||||
Assert.Equal(MockText, data.UserName);
|
||||
Assert.Equal(MockText, data.OpenId);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "解密响应中的敏感数据([GET] /transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id})")]
|
||||
public void DecryptResponseSensitiveProperty_GetTransferBatchDetailByDetailIdResponse()
|
||||
{
|
||||
var mock = new Models.GetTransferBatchDetailByDetailIdResponse()
|
||||
{
|
||||
RawStatus = 200,
|
||||
UserName = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText),
|
||||
OpenId = MockText
|
||||
};
|
||||
var data = MockClientInstance.Value.DecryptResponseSensitiveProperty(mock);
|
||||
|
||||
Assert.Equal(MockText, data.UserName);
|
||||
Assert.Equal(MockText, data.OpenId);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "解密响应中的敏感数据([GET] /partner-transfer/batches/batch-id/{batch_id}/details/detail-id/{detail_id})")]
|
||||
public void DecryptResponseSensitiveProperty_GetPartnerTransferBatchDetailByDetailIdResponse()
|
||||
{
|
||||
var mock = new Models.GetPartnerTransferBatchDetailByDetailIdResponse()
|
||||
{
|
||||
RawStatus = 200,
|
||||
UserName = Utilities.RSAUtility.EncryptWithECB(RSA_PUBLIC_KEY, MockText),
|
||||
OpenId = MockText
|
||||
};
|
||||
var data = MockClientInstance.Value.DecryptResponseSensitiveProperty(mock);
|
||||
|
||||
Assert.Equal(MockText, data.UserName);
|
||||
Assert.Equal(MockText, data.OpenId);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.UnitTests
|
||||
Assert.NotNull(response.WechatpayCertSerialNumber);
|
||||
Assert.NotNull(response.WechatpaySignature);
|
||||
|
||||
TestClients.Instance.DecryptResponseEncryptedData(ref response);
|
||||
response = TestClients.Instance.DecryptResponseSensitiveProperty(response);
|
||||
foreach (var certificateModel in response.CertificateList)
|
||||
{
|
||||
TestClients.GlobalCertificateManager.AddEntry(new Settings.CertificateEntry(certificateModel));
|
||||
|
Loading…
Reference in New Issue
Block a user