fix(tenpayv3): 修复部分响应模型解密敏感数据字段时抛出异常的问题

This commit is contained in:
Fu Diwei 2021-12-03 16:27:13 +08:00
parent 9423dd5642
commit ec6e3f7e7a
28 changed files with 120 additions and 58 deletions

View File

@ -0,0 +1,12 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class WechatTenpaySensitiveAttribute : Attribute
{
public WechatTenpaySensitiveAttribute()
{
}
}
}

View File

@ -23,55 +23,59 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
try
{
// 遍历并加密被标记为敏感数据的字段
Utilities.ReflectionUtility.ReplacePropertyStringValue(ref request, (obj, prop, value) =>
bool requireEncrypt = request.GetType().GetCustomAttributes<WechatTenpaySensitiveAttribute>(inherit: true).Any();
if (requireEncrypt)
{
var attr = prop.GetCustomAttribute<WechatTenpaySensitivePropertyAttribute>();
if (attr == null)
return value;
if (Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB.Equals(attr.Algorithm))
// 遍历并加密被标记为敏感数据的字段
Utilities.ReflectionUtility.ReplacePropertyStringValue(ref request, (obj, prop, value) =>
{
if (client.CertificateManager == null)
throw new Exceptions.WechatTenpayRequestEncryptionException("Encrypt request failed, because there is no platform certificate in the manager.");
var attr = prop.GetCustomAttribute<WechatTenpaySensitivePropertyAttribute>();
if (attr == null)
return value;
string certificate;
if (!string.IsNullOrEmpty(request.WechatpayCertSerialNumber))
if (Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB.Equals(attr.Algorithm))
{
// 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值
var cert = client.CertificateManager.GetEntry(request.WechatpayCertSerialNumber!);
if (!cert.HasValue)
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))
{
throw new Exceptions.WechatTenpayEventVerificationException("Encrypt request failed, because there is no platform certificate matched the serial number.");
// 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值
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.");
}
var cert = certs.First();
certificate = cert.Certificate;
request.WechatpayCertSerialNumber = cert.SerialNumber;
}
certificate = cert.Value.Certificate;
return Utilities.RSAUtility.EncryptWithECBByCertificate(
certificate: certificate,
plainText: value
);
}
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.");
}
var cert = certs.First();
certificate = cert.Certificate;
request.WechatpayCertSerialNumber = cert.SerialNumber;
throw new Exceptions.WechatTenpayRequestEncryptionException("Unsupported encryption algorithm.");
}
return Utilities.RSAUtility.EncryptWithECBByCertificate(
certificate: certificate,
plainText: value
);
}
else
{
throw new Exceptions.WechatTenpayRequestEncryptionException("Unsupported encryption algorithm.");
}
});
});
}
}
catch (Exception ex) when (!(ex is Exceptions.WechatTenpayRequestEncryptionException))
{

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Reflection;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
@ -54,25 +55,29 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
return response;
}
// 遍历并解密被标记为敏感数据的字段
Utilities.ReflectionUtility.ReplacePropertyStringValue(ref response, (obj, prop, value) =>
bool requireDecrypt = response.GetType().GetCustomAttributes<WechatTenpaySensitiveAttribute>(inherit: true).Any();
if (requireDecrypt)
{
var attr = prop.GetCustomAttribute<WechatTenpaySensitivePropertyAttribute>();
if (attr == null)
return value;
// 遍历并解密被标记为敏感数据的字段
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
{
throw new Exceptions.WechatTenpayResponseDecryptionException("Unsupported decryption algorithm.");
}
});
if (Constants.EncryptionAlgorithms.RSA_2048_PKCS8_ECB.Equals(attr.Algorithm))
{
return Utilities.RSAUtility.DecryptWithECB(
privateKey: client.Credentials.MerchantCertPrivateKey,
cipherText: value
);
}
else
{
throw new Exceptions.WechatTenpayResponseDecryptionException("Unsupported decryption algorithm.");
}
});
}
}
catch (Exception ex) when (!(ex is Exceptions.WechatTenpayResponseDecryptionException))
{

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [POST] /applyment4sub/applyment/ 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class CreateApplyForSubMerchantApplymentRequest : WechatTenpayRequest
{
public static class Types

View File

@ -3,6 +3,7 @@
/// <summary>
/// <para>表示 [POST] /apply4sub/sub_merchants/{sub_mchid}/modify-settlement 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class ModifyApplyForSubMerchantSettlementRequest : WechatTenpayRequest
{
/// <summary>

View File

@ -5,6 +5,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [POST] /apply4subject/applyment 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class CreateApplyForSubjectApplymentRequest : WechatTenpayRequest
{
public static class Types

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /bill/sub-merchant-fundflowbill 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class GetBillSubMerchantFundflowBillResponse : WechatTenpayResponse
{
public static class Types

View File

@ -5,6 +5,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [POST] /brand/profitsharing/orders 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class CreateBrandProfitSharingOrderRequest : WechatTenpayRequest
{
public static class Types

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /certificates 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class QueryCertificatesResponse : WechatTenpayResponse
{
public static class Types

View File

@ -5,6 +5,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [POST] /ecommerce/applyments 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class CreateEcommerceApplymentRequest : WechatTenpayRequest
{
public static class Types

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /ecommerce/applyments/out-request-no/{out_request_no} 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class GetEcommerceApplymentByOutRequestNumberResponse : WechatTenpayResponse
{
public static class Types

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /ecommerce/bill/fundflowbill 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class GetEcommerceBillFundflowBillResponse : WechatTenpayResponse
{
public static class Types

View File

@ -5,6 +5,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [POST] /ecommerce/profitsharing/orders 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class CreateEcommerceProfitSharingOrderRequest : WechatTenpayRequest
{
public static class Types

View File

@ -3,6 +3,7 @@
/// <summary>
/// <para>表示 [POST] /ecommerce/profitsharing/receivers/add 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class AddEcommerceProfitSharingReceiverRequest : WechatTenpayRequest
{
/// <summary>

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /merchant-service/complaints-v2/{complaint_id} 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class GetMerchantServiceComplaintByComplaintIdResponse : WechatTenpayResponse
{
public static class Types

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /merchant-service/complaints-v2 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class QueryMerchantServiceComplaintsResponse : WechatTenpayResponse
{
public static class Types

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /partner-transfer/batches/out-batch-no/{out_batch_no}/details/out-detail-no/{out_detail_no} 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class GetPartnerTransferBatchDetailByOutDetailNumberResponse : GetTransferBatchDetailByOutDetailNumberResponse
{
/// <summary>

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /payscore/merchant-bill 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class GetPayScoreMerchantBillResponse : WechatTenpayResponse
{
public static class Types

View File

@ -5,6 +5,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [POST] /profitsharing/orders 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class CreateProfitSharingOrderRequest : WechatTenpayRequest
{
public static class Types

View File

@ -3,6 +3,7 @@
/// <summary>
/// <para>表示 [POST] /profitsharing/receivers/add 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class AddProfitSharingReceiverRequest : WechatTenpayRequest
{
/// <summary>

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [POST] /profitsharing/receivers/add 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class AddProfitSharingReceiverResponse : WechatTenpayResponse
{
/// <summary>

View File

@ -3,6 +3,7 @@
/// <summary>
/// <para>表示 [POST] /smartguide/guides 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class CreateSmartGuideRequest : WechatTenpayRequest
{
/// <summary>

View File

@ -3,6 +3,7 @@
/// <summary>
/// <para>表示 [GET] /smartguide/guides 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class QuerySmartGuidesRequest : WechatTenpayRequest
{
/// <summary>

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /smartguide/guides 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class QuerySmartGuidesResponse : WechatTenpayResponse
{
public static class Types

View File

@ -3,6 +3,7 @@
/// <summary>
/// <para>表示 [PATCH] /smartguide/guides/{guide_id} 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class UpdateSmartGuideRequest : WechatTenpayRequest
{
/// <summary>

View File

@ -5,6 +5,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [POST] /transfer/batches 接口的请求。</para>
/// </summary>
[WechatTenpaySensitive]
public class CreateTransferBatchRequest : WechatTenpayRequest
{
public static class Types

View File

@ -6,6 +6,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
/// <summary>
/// <para>表示 [GET] /transfer/batches/out-batch-no/{out_batch_no}/details/out-detail-no/{out_detail_no} 接口的响应。</para>
/// </summary>
[WechatTenpaySensitive]
public class GetTransferBatchDetailByOutDetailNumberResponse : WechatTenpayResponse
{
/// <summary>

View File

@ -66,6 +66,9 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
if (objType.IsArray)
{
var array = (obj as Array)!;
if (array.IsReadOnly)
return;
for (int i = 0, len = array.Length; i < len; i++)
{
object? element = array.GetValue(i);
@ -77,9 +80,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
{
if (currentProp == null)
continue;
if (!currentProp.CanWrite)
continue;
string oldValue = (string)element!;
string newValue = replacement(obj, currentProp, oldValue);
string newValue = replacement(obj!, currentProp, oldValue);
array.SetValue(newValue, i);
}
else if (elementType.IsClass)
@ -93,8 +98,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
}
}
}
else if (obj is IList list)
else if (obj is IList)
{
var list = (obj as IList)!;
if (list.IsReadOnly)
return;
for (int i = 0, len = list.Count; i < len; i++)
{
object? element = list[i];
@ -106,6 +115,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
{
if (currentProp == null)
continue;
if (!currentProp.CanWrite)
continue;
string oldValue = (string)element!;
string newValue = replacement(obj, currentProp, oldValue);
@ -122,8 +133,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
}
}
}
else if (obj is IDictionary dict)
else if (obj is IDictionary)
{
var dict = (obj as IDictionary)!;
if (dict.IsReadOnly)
return;
foreach (DictionaryEntry entry in dict)
{
object? entryValue = entry.Value;
@ -135,6 +150,8 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
{
if (currentProp == null)
continue;
if (!currentProp.CanWrite)
continue;
string oldValue = (string)entryValue!;
string newValue = replacement(obj, currentProp, oldValue);