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
2025-09-23 22:50:12 +08:00
bool required = request . GetType ( ) . IsDefined ( typeof ( WechatTenpaySensitiveAttribute ) ) ;
if ( ! required )
return request ;
2024-11-05 21:19:50 +08:00
2025-09-23 22:50:12 +08:00
try
{
switch ( client . PlatformAuthScheme )
{
case PlatformAuthScheme . Certificate :
{
WechatTenpayClientRequestSerialNumberExtensions . _EnsureRequestWechatpaySerialNumberIsSet ( client , request ) ;
return EncryptRequestSensitivePropertyUseCertificateManager < TRequest > ( client . PlatformCertificateManager , client . Credentials . SignScheme , request ) ;
}
case PlatformAuthScheme . PublicKey :
{
WechatTenpayClientRequestSerialNumberExtensions . _EnsureRequestWechatpaySerialNumberIsSet ( client , request ) ;
return EncryptRequestSensitivePropertyUsePublicKeyManager < TRequest > ( client . PlatformPublicKeyManager , client . Credentials . SignScheme , request ) ;
}
default :
throw new NotSupportedException ( $"Unsupported platform auth scheme: \" { client . PlatformAuthScheme } \ "." ) ;
}
}
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
}
}
/// <summary>
/// <para>异步加密请求中传入的敏感数据。该方法会改变传入的请求模型对象。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
2025-09-23 22:50:12 +08:00
public static async Task < TRequest > EncryptRequestSensitivePropertyAsync < TRequest > ( this WechatTenpayClient client , TRequest request , CancellationToken cancellationToken = default )
2024-11-05 21:19:50 +08:00
where TRequest : WechatTenpayRequest
{
if ( client is null ) throw new ArgumentNullException ( nameof ( client ) ) ;
if ( request is null ) throw new ArgumentNullException ( nameof ( request ) ) ;
2025-09-23 22:50:12 +08:00
bool required = request . GetType ( ) . IsDefined ( typeof ( WechatTenpaySensitiveAttribute ) ) ;
if ( ! required )
return request ;
2024-11-05 21:19:50 +08:00
2024-02-04 11:39:32 +08:00
try
{
2025-09-23 22:50:12 +08:00
switch ( client . PlatformAuthScheme )
2024-11-05 21:19:50 +08:00
{
2025-09-23 22:50:12 +08:00
case PlatformAuthScheme . Certificate :
{
if ( client . PlatformCertificateManager is not ICertificateManagerAsync )
{
// 降级为同步调用
return EncryptRequestSensitivePropertyUseCertificateManager ( client . PlatformCertificateManager , client . Credentials . SignScheme , request ) ;
}
await WechatTenpayClientRequestSerialNumberExtensions . _EnsureRequestWechatpaySerialNumberIsSetAsync ( client , request , cancellationToken ) . ConfigureAwait ( false ) ;
return await EncryptRequestSensitivePropertyUseCertificateManagerAsync < TRequest > ( ( ICertificateManagerAsync ) client . PlatformCertificateManager , client . Credentials . SignScheme , request , cancellationToken ) . ConfigureAwait ( false ) ;
}
case PlatformAuthScheme . PublicKey :
{
if ( client . PlatformPublicKeyManager is not IPublicKeyManagerAsync )
{
// 降级为同步调用
return EncryptRequestSensitivePropertyUsePublicKeyManager ( client . PlatformPublicKeyManager , client . Credentials . SignScheme , request ) ;
}
await WechatTenpayClientRequestSerialNumberExtensions . _EnsureRequestWechatpaySerialNumberIsSetAsync ( client , request , cancellationToken ) . ConfigureAwait ( false ) ;
return await EncryptRequestSensitivePropertyUsePublicKeyManagerAsync < TRequest > ( ( IPublicKeyManagerAsync ) client . PlatformPublicKeyManager , client . Credentials . SignScheme , request , cancellationToken ) . ConfigureAwait ( false ) ;
}
default :
throw new NotSupportedException ( $"Unsupported platform auth scheme: \" { client . PlatformAuthScheme } \ "." ) ;
2024-11-05 21:19:50 +08:00
}
2024-02-06 11:23:04 +08:00
}
catch ( WechatTenpayException )
{
throw ;
}
catch ( Exception ex )
{
throw new WechatTenpayException ( "Failed to encrypt request. Please see the inner exception for more details." , ex ) ;
}
}
2025-09-23 22:50:12 +08:00
private static TRequest EncryptRequestSensitivePropertyUseCertificateManager < TRequest > ( ICertificateManager manager , string signScheme , TRequest request )
2024-02-06 11:23:04 +08:00
where TRequest : WechatTenpayRequest
{
2025-09-23 22:50:12 +08:00
if ( manager is null )
throw new NullReferenceException ( "The platform certificate manager is not configured." ) ;
2024-02-06 11:23:04 +08:00
2025-09-23 22:50:12 +08:00
CertificateEntry ? entry = manager . 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." ) ;
2024-02-06 11:23:04 +08:00
2025-09-23 22:50:12 +08:00
return PopulateRequestEncryptedFieldsByCertificate ( signScheme , entry . Value . Certificate , ref request ) ;
2024-11-05 21:19:50 +08:00
}
2025-09-23 22:50:12 +08:00
private static async Task < TRequest > EncryptRequestSensitivePropertyUseCertificateManagerAsync < TRequest > ( ICertificateManagerAsync manager , string signScheme , TRequest request , CancellationToken cancellationToken = default )
2024-11-05 21:19:50 +08:00
where TRequest : WechatTenpayRequest
{
2025-09-23 22:50:12 +08:00
if ( manager is null )
throw new NullReferenceException ( "The platform certificate manager is not configured." ) ;
2024-11-05 21:19:50 +08:00
2025-09-23 22:50:12 +08:00
CertificateEntry ? entry = await manager . 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." ) ;
2024-11-05 21:19:50 +08:00
2025-09-23 22:50:12 +08:00
return PopulateRequestEncryptedFieldsByCertificate ( signScheme , entry . Value . Certificate , ref request ) ;
2024-11-05 21:19:50 +08:00
}
2025-09-23 22:50:12 +08:00
private static TRequest EncryptRequestSensitivePropertyUsePublicKeyManager < TRequest > ( IPublicKeyManager manager , string signScheme , TRequest request )
2024-11-05 21:19:50 +08:00
where TRequest : WechatTenpayRequest
{
2025-09-23 22:50:12 +08:00
if ( manager is null )
throw new NullReferenceException ( "The platform public key manager is not configured." ) ;
2024-11-05 21:19:50 +08:00
2025-09-23 22:50:12 +08:00
PublicKeyEntry ? entry = manager . GetEntry ( request . WechatpaySerialNumber ! ) ;
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 } \ "." ) ;
2024-11-05 21:19:50 +08:00
2025-09-23 22:50:12 +08:00
return PopulateRequestEncryptedFieldsByPublicKey ( signScheme , entry . Value . PublicKey , ref request ) ;
}
2024-11-05 21:19:50 +08:00
2025-09-23 22:50:12 +08:00
private static async Task < TRequest > EncryptRequestSensitivePropertyUsePublicKeyManagerAsync < TRequest > ( IPublicKeyManagerAsync manager , string signScheme , TRequest request , CancellationToken cancellationToken = default )
where TRequest : WechatTenpayRequest
{
if ( manager is null )
throw new NullReferenceException ( "The platform public key manager is not configured." ) ;
2024-11-05 21:19:50 +08:00
2025-09-23 22:50:12 +08:00
PublicKeyEntry ? entry = await manager . 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 } \ "." ) ;
2024-11-05 21:19:50 +08:00
2025-09-23 22:50:12 +08:00
return PopulateRequestEncryptedFieldsByPublicKey ( signScheme , entry . Value . PublicKey , ref request ) ;
2021-11-25 18:42:54 +08:00
}
2024-02-06 11:23:04 +08:00
2025-09-23 22:50:12 +08:00
private static TRequest PopulateRequestEncryptedFieldsByCertificate < TRequest > ( string scheme , string certificate , ref TRequest request )
where TRequest : WechatTenpayRequest
2024-02-06 11:23:04 +08:00
{
2025-09-23 22:50:12 +08:00
switch ( scheme )
2024-02-06 11:23:04 +08:00
{
2025-09-23 22:50:12 +08:00
case SignSchemes . WECHATPAY2_RSA_2048_WITH_SHA256 :
2024-11-05 21:19:50 +08:00
{
string publicKey = RSAUtility . ExportPublicKeyFromCertificate ( certificate ) ;
2025-09-23 22:50:12 +08:00
return PopulateRequestEncryptedFieldsByPublicKey ( scheme , publicKey , ref request ) ;
2024-11-05 21:19:50 +08:00
}
2025-09-23 22:50:12 +08:00
case SignSchemes . WECHATPAY2_SM2_WITH_SM3 :
2024-11-05 21:19:50 +08:00
{
string publicKey = SM2Utility . ExportPublicKeyFromCertificate ( certificate ) ;
2025-09-23 22:50:12 +08:00
return PopulateRequestEncryptedFieldsByPublicKey ( scheme , publicKey , ref request ) ;
2024-11-05 21:19:50 +08:00
}
default :
2025-09-23 22:50:12 +08:00
throw new NotSupportedException ( $"Unsupported signing scheme: \" { scheme } \ "." ) ;
2024-11-05 21:19:50 +08:00
}
}
2025-09-23 22:50:12 +08:00
private static TRequest PopulateRequestEncryptedFieldsByPublicKey < TRequest > ( string scheme , string publicKey , ref TRequest request )
where TRequest : WechatTenpayRequest
2024-11-05 21:19:50 +08:00
{
2025-09-23 22:50:12 +08:00
ReflectionHelper . ReplaceObjectStringProperties ( ref request , ( _ , currentProp , oldValue ) = >
2024-11-05 21:19:50 +08:00
{
2025-09-23 22:50:12 +08:00
if ( currentProp is null | | ! currentProp . IsDefined ( typeof ( WechatTenpaySensitivePropertyAttribute ) ) )
return ( false , oldValue ) ;
2024-02-06 11:23:04 +08:00
2025-09-23 22:50:12 +08:00
WechatTenpaySensitivePropertyAttribute ? attribute = currentProp
. GetCustomAttributes < WechatTenpaySensitivePropertyAttribute > ( )
. FirstOrDefault ( attr = > attr . Scheme = = scheme ) ;
if ( attribute is null )
return ( false , oldValue ) ;
string newValue ;
switch ( attribute . Algorithm )
{
case EncryptionAlgorithms . RSA_2048_ECB_PKCS8_OAEP_WITH_SHA1_AND_MGF1 :
newValue = RSAUtility . EncryptWithECB (
publicKeyPem : publicKey ,
plainData : oldValue ,
paddingMode : RSAUtility . PADDING_MODE_OAEPWITHSHA1ANDMGF1
) ! ;
break ;
case EncryptionAlgorithms . RSA_2048_ECB_PKCS1 :
newValue = RSAUtility . EncryptWithECB (
publicKeyPem : publicKey ,
plainData : oldValue ,
paddingMode : RSAUtility . PADDING_MODE_PKCS1
) ! ;
break ;
case EncryptionAlgorithms . SM2_C1C3C2_ASN1 :
newValue = SM2Utility . Encrypt (
publicKeyPem : publicKey ,
plainData : oldValue ,
asn1Encoding : true
) ! ;
break ;
default :
throw new NotSupportedException ( $"Unsupported encryption algorithm: \" { attribute . Algorithm } \ "." ) ;
}
return ( true , newValue ) ;
} ) ;
return request ;
2024-02-06 11:23:04 +08:00
}
2021-11-25 18:42:54 +08:00
}
}