2022-11-13 23:17:18 +08:00
using System ;
2023-03-30 21:40:48 +08:00
using System.Collections.Generic ;
2021-11-25 18:42:54 +08:00
using System.Linq ;
using System.Reflection ;
2024-02-06 11:23:04 +08:00
using System.Threading ;
using System.Threading.Tasks ;
2021-11-25 18:42:54 +08:00
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
2024-02-06 11:23:04 +08:00
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants ;
2023-03-30 21:40:48 +08:00
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings ;
2024-02-04 11:39:32 +08:00
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities ;
2023-03-30 21:40:48 +08:00
2021-11-25 18:42:54 +08:00
public static class WechatTenpayClientRequestEncryptionExtensions
{
2024-02-04 11:39:32 +08:00
/// <summary>
/// <para>加密请求中传入的敏感数据。该方法会改变传入的请求模型对象。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <returns></returns>
public static TRequest EncryptRequestSensitiveProperty < TRequest > ( this WechatTenpayClient client , TRequest request )
2022-11-13 23:17:18 +08:00
where TRequest : WechatTenpayRequest
{
2024-02-04 11:39:32 +08:00
if ( client is null ) throw new ArgumentNullException ( nameof ( client ) ) ;
if ( request is null ) throw new ArgumentNullException ( nameof ( request ) ) ;
2022-11-13 23:17:18 +08:00
2024-11-05 21:19:50 +08:00
switch ( client . PlatformAuthScheme )
{
case PlatformAuthScheme . Certificate :
return EncryptRequestSensitivePropertyByCertificate < TRequest > ( client , request ) ;
case PlatformAuthScheme . PublicKey :
return EncryptRequestSensitivePropertyByPublicKey < TRequest > ( client , request ) ;
default :
throw new WechatTenpayException ( $"Unsupported platform auth scheme: \" { client . PlatformAuthScheme } \ "." ) ;
}
}
/// <summary>
/// <para>异步加密请求中传入的敏感数据。该方法会改变传入的请求模型对象。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static Task < TRequest > EncryptRequestSensitivePropertyAsync < TRequest > ( this WechatTenpayClient client , TRequest request , CancellationToken cancellationToken = default )
where TRequest : WechatTenpayRequest
{
if ( client is null ) throw new ArgumentNullException ( nameof ( client ) ) ;
if ( request is null ) throw new ArgumentNullException ( nameof ( request ) ) ;
switch ( client . PlatformAuthScheme )
{
case PlatformAuthScheme . Certificate :
return EncryptRequestSensitivePropertyByCertificateAsync < TRequest > ( client , request ) ;
case PlatformAuthScheme . PublicKey :
return EncryptRequestSensitivePropertyByPublicKeyAsync < TRequest > ( client , request ) ;
default :
throw new WechatTenpayException ( $"Unsupported platform auth scheme: \" { client . PlatformAuthScheme } \ "." ) ;
}
}
private static TRequest EncryptRequestSensitivePropertyByCertificate < TRequest > ( this WechatTenpayClient client , TRequest request )
where TRequest : WechatTenpayRequest
{
2024-02-04 11:39:32 +08:00
try
{
bool requireEncrypt = request . GetType ( ) . IsDefined ( typeof ( WechatTenpaySensitiveAttribute ) ) ;
if ( ! requireEncrypt )
return request ;
2022-11-13 23:17:18 +08:00
2024-02-04 11:39:32 +08:00
string signScheme = client . Credentials . SignScheme ;
string algorithmType = // 签名方式与加密算法保持一致: RSA_SHA256 签名需 RSA 加密, SM3 签名需 SM2 加密
2024-02-06 11:23:04 +08:00
SignSchemes . WECHATPAY2_RSA_2048_WITH_SHA256 . Equals ( signScheme ) ? CertificateEntry . ALGORITHM_TYPE_RSA :
SignSchemes . WECHATPAY2_SM2_WITH_SM3 . Equals ( signScheme ) ? CertificateEntry . ALGORITHM_TYPE_SM2 :
2024-02-04 11:39:32 +08:00
throw new WechatTenpayException ( $"Failed to encrypt request. Unsupported signing scheme: \" { signScheme } \ "." ) ;
2022-11-13 23:17:18 +08:00
2024-11-05 21:19:50 +08:00
string certificate ;
if ( string . IsNullOrEmpty ( request . WechatpaySerialNumber ) )
{
// 如果未在请求中指定特定的平台证书序列号,从管理器中取过期时间最远的
IEnumerable < CertificateEntry > entries = client . PlatformCertificateManager . AllEntries ( )
. Where ( e = > e . AlgorithmType = = algorithmType )
. OrderByDescending ( e = > e . ExpireTime ) ;
if ( ! entries . Any ( ) )
{
throw new WechatTenpayException ( "Failed to encrypt request, because the platform certificate manager is empty. Please make sure you have downloaded platform (NOT merchant) certificates first." ) ;
}
CertificateEntry entry = entries . First ( ) ;
certificate = entry . Certificate ;
request . WechatpaySerialNumber = entry . SerialNumber ;
}
else
{
// 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值
CertificateEntry ? entry = client . PlatformCertificateManager . GetEntry ( request . WechatpaySerialNumber ! ) ;
if ( ! entry . HasValue )
{
throw new WechatTenpayException ( $"Failed to encrypt request, because the platform certificate manager does not contain a certificate matched the serial number \" { request . WechatpaySerialNumber } \ ". Please make sure you have downloaded platform (NOT merchant) certificates first." ) ;
}
certificate = entry . Value . Certificate ;
}
2024-02-04 11:39:32 +08:00
ReflectionHelper . ReplaceObjectStringProperties ( request , ( _ , currentProp , oldValue ) = >
2022-11-13 23:17:18 +08:00
{
2024-02-04 11:39:32 +08:00
if ( currentProp is null | | ! currentProp . IsDefined ( typeof ( WechatTenpaySensitivePropertyAttribute ) ) )
return ( false , oldValue ) ;
2022-11-13 23:17:18 +08:00
2024-02-04 11:39:32 +08:00
WechatTenpaySensitivePropertyAttribute ? attribute = currentProp
. GetCustomAttributes < WechatTenpaySensitivePropertyAttribute > ( )
. FirstOrDefault ( attr = > attr . Scheme = = signScheme ) ;
if ( attribute is null )
return ( false , oldValue ) ;
2022-11-13 23:17:18 +08:00
2024-02-06 11:23:04 +08:00
string newValue = GenerateEncryptedValueByCertificate ( attribute . Algorithm , certificate , oldValue ) ;
return ( true , newValue ) ;
} ) ;
return request ;
}
catch ( WechatTenpayException )
{
throw ;
}
catch ( Exception ex )
{
throw new WechatTenpayException ( "Failed to encrypt request. Please see the inner exception for more details." , ex ) ;
}
}
2024-11-05 21:19:50 +08:00
private static async Task < TRequest > EncryptRequestSensitivePropertyByCertificateAsync < TRequest > ( this WechatTenpayClient client , TRequest request , CancellationToken cancellationToken = default )
2024-02-06 11:23:04 +08:00
where TRequest : WechatTenpayRequest
{
if ( client . PlatformCertificateManager is not ICertificateManagerAsync )
{
// 降级为同步调用
2024-11-05 21:19:50 +08:00
return EncryptRequestSensitivePropertyByCertificate ( client , request ) ;
2024-02-06 11:23:04 +08:00
}
try
{
bool requireEncrypt = request . GetType ( ) . IsDefined ( typeof ( WechatTenpaySensitiveAttribute ) ) ;
if ( ! requireEncrypt )
2024-11-05 21:19:50 +08:00
return request ;
2024-02-06 11:23:04 +08:00
string signScheme = client . Credentials . SignScheme ;
string algorithmType = // 签名方式与加密算法保持一致: RSA_SHA256 签名需 RSA 加密, SM3 签名需 SM2 加密
SignSchemes . WECHATPAY2_RSA_2048_WITH_SHA256 . Equals ( signScheme ) ? CertificateEntry . ALGORITHM_TYPE_RSA :
SignSchemes . WECHATPAY2_SM2_WITH_SM3 . Equals ( signScheme ) ? CertificateEntry . ALGORITHM_TYPE_SM2 :
throw new WechatTenpayException ( $"Failed to encrypt request. Unsupported signing scheme: \" { signScheme } \ "." ) ;
2024-11-05 21:19:50 +08:00
string certificate ;
if ( string . IsNullOrEmpty ( request . WechatpaySerialNumber ) )
{
if ( client . PlatformCertificateManager is null )
throw new WechatTenpayException ( "Failed to encrypt request, because the platform certificate manager is not initialized." ) ;
// 如果未在请求中指定特定的平台证书序列号,从管理器中取过期时间最远的
IEnumerable < CertificateEntry > entries = await ( ( ICertificateManagerAsync ) client . PlatformCertificateManager ) . AllEntriesAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
entries = entries . Where ( e = > e . AlgorithmType = = algorithmType ) . OrderByDescending ( e = > e . ExpireTime ) ;
if ( ! entries . Any ( ) )
{
throw new WechatTenpayException ( "Failed to encrypt request, because the platform certificate manager is empty. Please make sure you have downloaded platform (NOT merchant) certificates first." ) ;
}
CertificateEntry entry = entries . First ( ) ;
certificate = entry . Certificate ;
request . WechatpaySerialNumber = entry . SerialNumber ;
}
else
{
// 如果已在请求中指定特定的平台证书序列号,直接从管理器中取值
CertificateEntry ? entry = await ( ( ICertificateManagerAsync ) client . PlatformCertificateManager ) . GetEntryAsync ( request . WechatpaySerialNumber ! , cancellationToken ) . ConfigureAwait ( false ) ;
if ( ! entry . HasValue )
{
throw new WechatTenpayException ( $"Failed to encrypt request, because the platform certificate manager does not contain a certificate matched the serial number \" { request . WechatpaySerialNumber } \ ". Please make sure you have downloaded platform (NOT merchant) certificates first." ) ;
}
certificate = entry . Value . Certificate ;
}
2024-02-06 11:23:04 +08:00
ReflectionHelper . ReplaceObjectStringProperties ( request , ( _ , currentProp , oldValue ) = >
{
if ( currentProp is null | | ! currentProp . IsDefined ( typeof ( WechatTenpaySensitivePropertyAttribute ) ) )
return ( false , oldValue ) ;
WechatTenpaySensitivePropertyAttribute ? attribute = currentProp
. GetCustomAttributes < WechatTenpaySensitivePropertyAttribute > ( )
. FirstOrDefault ( attr = > attr . Scheme = = signScheme ) ;
if ( attribute is null )
return ( false , oldValue ) ;
2024-11-05 21:19:50 +08:00
string newValue = GenerateEncryptedValueByCertificate ( attribute . Algorithm , certificate , oldValue ) ;
return ( true , newValue ) ;
} ) ;
return request ;
}
catch ( WechatTenpayException )
{
throw ;
}
catch ( Exception ex )
{
throw new WechatTenpayException ( "Failed to encrypt request. Please see the inner exception for more details." , ex ) ;
}
}
private static TRequest EncryptRequestSensitivePropertyByPublicKey < TRequest > ( this WechatTenpayClient client , TRequest request )
where TRequest : WechatTenpayRequest
{
try
{
bool requireEncrypt = request . GetType ( ) . IsDefined ( typeof ( WechatTenpaySensitiveAttribute ) ) ;
if ( ! requireEncrypt )
return request ;
string signScheme = client . Credentials . SignScheme ;
string algorithmType = // 签名方式与加密算法保持一致: RSA_SHA256 签名需 RSA 加密, SM3 签名需 SM2 加密
SignSchemes . WECHATPAY2_RSA_2048_WITH_SHA256 . Equals ( signScheme ) ? PublicKeyEntry . ALGORITHM_TYPE_RSA :
SignSchemes . WECHATPAY2_SM2_WITH_SM3 . Equals ( signScheme ) ? PublicKeyEntry . ALGORITHM_TYPE_SM2 :
throw new WechatTenpayException ( $"Failed to encrypt request. Unsupported signing scheme: \" { signScheme } \ "." ) ;
string publicKey ;
if ( string . IsNullOrEmpty ( request . WechatpaySerialNumber ) )
{
2025-01-25 20:10:30 +08:00
// 如果未在请求中指定特定的平台公钥 ID, 从管理器中取第一个
2024-11-05 21:19:50 +08:00
IEnumerable < PublicKeyEntry > entries = client . PlatformPublicKeyManager . AllEntries ( )
. Where ( e = > e . AlgorithmType = = algorithmType ) ;
if ( ! entries . Any ( ) )
2021-11-25 18:42:54 +08:00
{
2024-11-05 21:19:50 +08:00
throw new WechatTenpayException ( "Failed to encrypt request, because the platform public key manager is empty." ) ;
2024-02-06 11:23:04 +08:00
}
2024-11-05 21:19:50 +08:00
PublicKeyEntry entry = entries . First ( ) ;
publicKey = entry . PublicKey ;
request . WechatpaySerialNumber = entry . SerialNumber ;
}
else
{
2025-01-25 20:10:30 +08:00
// 如果已在请求中指定特定的平台公钥 ID, 直接从管理器中取值
2024-11-05 21:19:50 +08:00
PublicKeyEntry ? entry = client . PlatformPublicKeyManager . GetEntry ( request . WechatpaySerialNumber ! ) ;
if ( ! entry . HasValue )
2024-02-06 11:23:04 +08:00
{
2024-11-05 21:19:50 +08:00
throw new WechatTenpayException ( $"Failed to encrypt request, because the platform public key manager does not contain a key matched the serial number \" { request . WechatpaySerialNumber } \ "." ) ;
2022-11-13 23:17:18 +08:00
}
2024-02-04 11:39:32 +08:00
2024-11-05 21:19:50 +08:00
publicKey = entry . Value . PublicKey ;
}
ReflectionHelper . ReplaceObjectStringProperties ( request , ( _ , currentProp , oldValue ) = >
{
if ( currentProp is null | | ! currentProp . IsDefined ( typeof ( WechatTenpaySensitivePropertyAttribute ) ) )
return ( false , oldValue ) ;
WechatTenpaySensitivePropertyAttribute ? attribute = currentProp
. GetCustomAttributes < WechatTenpaySensitivePropertyAttribute > ( )
. FirstOrDefault ( attr = > attr . Scheme = = signScheme ) ;
if ( attribute is null )
return ( false , oldValue ) ;
string newValue = GenerateEncryptedValueByPublicKey ( attribute . Algorithm , publicKey , oldValue ) ;
2024-02-04 11:39:32 +08:00
return ( true , newValue ) ;
} ) ;
2022-11-13 23:17:18 +08:00
2024-11-05 21:19:50 +08:00
return request ;
}
catch ( WechatTenpayException )
{
throw ;
}
catch ( Exception ex )
{
throw new WechatTenpayException ( "Failed to encrypt request. Please see the inner exception for more details." , ex ) ;
}
}
private static async Task < TRequest > EncryptRequestSensitivePropertyByPublicKeyAsync < TRequest > ( this WechatTenpayClient client , TRequest request , CancellationToken cancellationToken = default )
where TRequest : WechatTenpayRequest
{
if ( client . PlatformPublicKeyManager is not IPublicKeyManagerAsync )
{
// 降级为同步调用
return EncryptRequestSensitivePropertyByPublicKey ( client , request ) ;
}
try
{
bool requireEncrypt = request . GetType ( ) . IsDefined ( typeof ( WechatTenpaySensitiveAttribute ) ) ;
if ( ! requireEncrypt )
return request ;
string signScheme = client . Credentials . SignScheme ;
string algorithmType = // 签名方式与加密算法保持一致: RSA_SHA256 签名需 RSA 加密, SM3 签名需 SM2 加密
SignSchemes . WECHATPAY2_RSA_2048_WITH_SHA256 . Equals ( signScheme ) ? PublicKeyEntry . ALGORITHM_TYPE_RSA :
SignSchemes . WECHATPAY2_SM2_WITH_SM3 . Equals ( signScheme ) ? PublicKeyEntry . ALGORITHM_TYPE_SM2 :
throw new WechatTenpayException ( $"Failed to encrypt request. Unsupported signing scheme: \" { signScheme } \ "." ) ;
string publicKey ;
if ( string . IsNullOrEmpty ( request . WechatpaySerialNumber ) )
{
if ( client . PlatformPublicKeyManager is null )
throw new WechatTenpayException ( "Failed to encrypt request, because the platform public key manager is not initialized." ) ;
2025-01-25 20:10:30 +08:00
// 如果未在请求中指定特定的平台公钥 ID, 从管理器中第一个
2024-11-05 21:19:50 +08:00
IEnumerable < PublicKeyEntry > entries = await ( ( IPublicKeyManagerAsync ) client . PlatformPublicKeyManager ) . AllEntriesAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
entries = entries . Where ( e = > e . AlgorithmType = = algorithmType ) ;
if ( ! entries . Any ( ) )
{
throw new WechatTenpayException ( "Failed to encrypt request, because the platform public key manager is empty." ) ;
}
PublicKeyEntry entry = entries . First ( ) ;
publicKey = entry . PublicKey ;
request . WechatpaySerialNumber = entry . SerialNumber ;
}
else
{
2025-01-25 20:10:30 +08:00
// 如果已在请求中指定特定的平台公钥 ID, 直接从管理器中取值
2024-11-05 21:19:50 +08:00
PublicKeyEntry ? entry = await ( ( IPublicKeyManagerAsync ) client . PlatformPublicKeyManager ) . GetEntryAsync ( request . WechatpaySerialNumber ! , cancellationToken ) . ConfigureAwait ( false ) ;
if ( ! entry . HasValue )
{
throw new WechatTenpayException ( $"Failed to encrypt request, because the platform public key manager does not contain a key matched the serial number \" { request . WechatpaySerialNumber } \ "." ) ;
}
publicKey = entry . Value . PublicKey ;
}
ReflectionHelper . ReplaceObjectStringProperties ( request , ( _ , currentProp , oldValue ) = >
{
if ( currentProp is null | | ! currentProp . IsDefined ( typeof ( WechatTenpaySensitivePropertyAttribute ) ) )
return ( false , oldValue ) ;
WechatTenpaySensitivePropertyAttribute ? attribute = currentProp
. GetCustomAttributes < WechatTenpaySensitivePropertyAttribute > ( )
. FirstOrDefault ( attr = > attr . Scheme = = signScheme ) ;
if ( attribute is null )
return ( false , oldValue ) ;
string newValue = GenerateEncryptedValueByPublicKey ( attribute . Algorithm , publicKey , oldValue ) ;
return ( true , newValue ) ;
} ) ;
return request ;
2021-11-25 18:42:54 +08:00
}
2022-11-13 23:17:18 +08:00
catch ( WechatTenpayException )
{
throw ;
}
catch ( Exception ex )
2021-11-25 18:42:54 +08:00
{
2024-01-29 23:12:37 +08:00
throw new WechatTenpayException ( "Failed to encrypt request. Please see the inner exception for more details." , ex ) ;
2021-11-25 18:42:54 +08:00
}
}
2024-02-06 11:23:04 +08:00
private static string GenerateEncryptedValueByCertificate ( string algorithm , string certificate , string value )
{
switch ( algorithm )
{
case EncryptionAlgorithms . RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1 :
2024-11-05 21:19:50 +08:00
case EncryptionAlgorithms . RSA_2048_ECB_PKCS1 :
{
string publicKey = RSAUtility . ExportPublicKeyFromCertificate ( certificate ) ;
return GenerateEncryptedValueByPublicKey ( algorithm , publicKey , value ) ;
}
case EncryptionAlgorithms . SM2_C1C3C2_ASN1 :
{
string publicKey = SM2Utility . ExportPublicKeyFromCertificate ( certificate ) ;
return GenerateEncryptedValueByPublicKey ( algorithm , publicKey , value ) ;
}
default :
throw new WechatTenpayException ( $"Failed to encrypt request. Unsupported encryption algorithm: \" { algorithm } \ "." ) ;
}
}
private static string GenerateEncryptedValueByPublicKey ( string algorithm , string publicKey , string value )
{
switch ( algorithm )
{
case EncryptionAlgorithms . RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1 :
return RSAUtility . EncryptWithECB (
publicKeyPem : publicKey ,
2024-02-06 11:23:04 +08:00
plainData : value ,
paddingMode : RSAUtility . PADDING_MODE_OAEPWITHSHA1ANDMGF1
) ! ;
case EncryptionAlgorithms . RSA_2048_ECB_PKCS1 :
2024-11-05 21:19:50 +08:00
return RSAUtility . EncryptWithECB (
publicKeyPem : publicKey ,
2024-02-06 11:23:04 +08:00
plainData : value ,
paddingMode : RSAUtility . PADDING_MODE_PKCS1
) ! ;
case EncryptionAlgorithms . SM2_C1C3C2_ASN1 :
2024-11-05 21:19:50 +08:00
return SM2Utility . Encrypt (
publicKeyPem : publicKey ,
2024-02-06 11:23:04 +08:00
plainData : value ,
asn1Encoding : true
) ! ;
default :
throw new WechatTenpayException ( $"Failed to encrypt request. Unsupported encryption algorithm: \" { algorithm } \ "." ) ;
}
}
2021-11-25 18:42:54 +08:00
}
}