2022-11-13 23:17:18 +08:00
using System ;
2024-02-06 11:23:04 +08:00
using System.Threading ;
using System.Threading.Tasks ;
2022-05-09 19:28:47 +08:00
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
2024-02-05 10:53:59 +08:00
using SKIT.FlurlHttpClient.Primitives ;
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-01-29 23:12:37 +08:00
internal static class WechatTenpayClientSigningExtensions
2022-05-09 19:28:47 +08:00
{
2024-02-05 15:58:42 +08:00
public static ErroredResult VerifySignature ( this WechatTenpayClient client , string strTimestamp , string strNonce , string strContent , string strSignature , string strSignScheme , string strSerialNumber )
2022-05-09 19:28:47 +08:00
{
2024-01-29 23:12:37 +08:00
if ( client is null ) throw new ArgumentNullException ( nameof ( client ) ) ;
2022-05-09 19:28:47 +08:00
2024-11-05 21:19:50 +08:00
switch ( client . PlatformAuthScheme )
2024-02-06 11:23:04 +08:00
{
2024-11-05 21:19:50 +08:00
case PlatformAuthScheme . Certificate :
{
CertificateEntry ? entry = client . PlatformCertificateManager . GetEntry ( strSerialNumber ) ;
if ( ! entry . HasValue )
{
return ErroredResult . Fail ( new Exception ( $"The platform certificate manager does not contain a certificate matched the serial number \" { strSerialNumber } \ "." ) ) ;
}
return GenerateSignatureResultByCertificate (
scheme : strSignScheme ,
certificate : entry . Value . Certificate ,
message : GenerateMessageForSignature ( timestamp : strTimestamp , nonce : strNonce , body : strContent ) ,
signature : strSignature
) ;
}
case PlatformAuthScheme . PublicKey :
{
PublicKeyEntry ? entry = client . PlatformPublicKeyManager . GetEntry ( strSerialNumber ) ;
if ( ! entry . HasValue )
{
return ErroredResult . Fail ( new Exception ( $"The platform public key manager does not contain a key matched the serial number \" { strSerialNumber } \ "." ) ) ;
}
return GenerateSignatureResultByPublicKey (
scheme : strSignScheme ,
publicKey : entry . Value . PublicKey ,
message : GenerateMessageForSignature ( timestamp : strTimestamp , nonce : strNonce , body : strContent ) ,
signature : strSignature
) ;
}
2024-02-06 11:23:04 +08:00
2024-11-05 21:19:50 +08:00
default :
return ErroredResult . Fail ( new Exception ( $"Unsupported platform auth scheme: \" { client . PlatformAuthScheme } \ "." ) ) ;
}
2024-02-06 11:23:04 +08:00
}
public static async Task < ErroredResult > VerifySignatureAsync ( this WechatTenpayClient client , string strTimestamp , string strNonce , string strContent , string strSignature , string strSignScheme , string strSerialNumber , CancellationToken cancellationToken = default )
{
if ( client is null ) throw new ArgumentNullException ( nameof ( client ) ) ;
2024-11-05 21:19:50 +08:00
switch ( client . PlatformAuthScheme )
2024-02-06 11:23:04 +08:00
{
2024-11-05 21:19:50 +08:00
case PlatformAuthScheme . Certificate :
{
if ( client . PlatformCertificateManager is not ICertificateManagerAsync )
{
// 降级为同步调用
return VerifySignature ( client , strTimestamp , strNonce , strContent , strSignature , strSignScheme , strSerialNumber ) ;
}
2024-02-06 11:23:04 +08:00
2024-11-05 21:19:50 +08:00
CertificateEntry ? entry = await ( ( ICertificateManagerAsync ) client . PlatformCertificateManager ) . GetEntryAsync ( strSerialNumber , cancellationToken ) . ConfigureAwait ( false ) ;
if ( ! entry . HasValue )
{
return ErroredResult . Fail ( new Exception ( $"The platform certificate manager does not contain a certificate matched the serial number \" { strSerialNumber } \ "." ) ) ;
}
return GenerateSignatureResultByCertificate (
scheme : strSignScheme ,
certificate : entry . Value . Certificate ,
message : GenerateMessageForSignature ( timestamp : strTimestamp , nonce : strNonce , body : strContent ) ,
signature : strSignature
) ;
}
case PlatformAuthScheme . PublicKey :
{
if ( client . PlatformCertificateManager is not IPublicKeyManagerAsync )
{
// 降级为同步调用
return VerifySignature ( client , strTimestamp , strNonce , strContent , strSignature , strSignScheme , strSerialNumber ) ;
}
PublicKeyEntry ? entry = await ( ( IPublicKeyManagerAsync ) client . PlatformPublicKeyManager ) . GetEntryAsync ( strSerialNumber , cancellationToken ) . ConfigureAwait ( false ) ;
if ( ! entry . HasValue )
{
return ErroredResult . Fail ( new Exception ( $"The platform public key manager does not contain a key matched the serial number \" { strSerialNumber } \ "." ) ) ;
}
return GenerateSignatureResultByPublicKey (
scheme : strSignScheme ,
publicKey : entry . Value . PublicKey ,
message : GenerateMessageForSignature ( timestamp : strTimestamp , nonce : strNonce , body : strContent ) ,
signature : strSignature
) ;
}
2024-02-06 11:23:04 +08:00
2024-11-05 21:19:50 +08:00
default :
return ErroredResult . Fail ( new Exception ( $"Unsupported platform auth scheme: \" { client . PlatformAuthScheme } \ "." ) ) ;
}
2024-02-06 11:23:04 +08:00
}
private static string GenerateMessageForSignature ( string timestamp , string nonce , string body )
{
return $"{timestamp}\n{nonce}\n{body}\n" ;
}
private static ErroredResult GenerateSignatureResultByCertificate ( string scheme , string certificate , string message , string signature )
2024-11-05 21:19:50 +08:00
{
string publicKey = string . Empty ;
switch ( scheme )
{
case SignSchemes . WECHATPAY2_RSA_2048_WITH_SHA256 :
{
try
{
publicKey = Utilities . RSAUtility . ExportPublicKeyFromCertificate ( certificate ) ;
}
catch ( Exception ex )
{
return ErroredResult . Fail ( ex ) ;
}
}
break ;
case SignSchemes . WECHATPAY2_SM2_WITH_SM3 :
{
try
{
publicKey = Utilities . SM2Utility . ExportPublicKeyFromCertificate ( certificate ) ;
}
catch ( Exception ex )
{
return ErroredResult . Fail ( ex ) ;
}
}
break ;
}
return GenerateSignatureResultByPublicKey ( scheme , publicKey , message , signature ) ;
}
private static ErroredResult GenerateSignatureResultByPublicKey ( string scheme , string publicKey , string message , string signature )
2024-02-06 11:23:04 +08:00
{
2024-02-05 15:58:42 +08:00
ErroredResult result ;
2024-02-06 11:23:04 +08:00
switch ( scheme )
2022-11-13 23:17:18 +08:00
{
2024-02-06 11:23:04 +08:00
case SignSchemes . WECHATPAY2_RSA_2048_WITH_SHA256 :
2022-11-13 23:17:18 +08:00
{
2024-02-05 15:58:42 +08:00
try
2022-11-13 23:17:18 +08:00
{
2024-11-05 21:19:50 +08:00
bool valid = Utilities . RSAUtility . VerifyWithSHA256 (
publicKeyPem : publicKey ,
2024-02-06 11:23:04 +08:00
messageData : message ,
encodingSignature : new EncodedString ( signature , EncodingKinds . Base64 )
2022-11-13 23:17:18 +08:00
) ;
2024-02-05 15:58:42 +08:00
if ( valid )
result = ErroredResult . Ok ( ) ;
else
2024-02-06 11:23:04 +08:00
result = ErroredResult . Fail ( new Exception ( $"Signature does not match. Maybe \" { signature } \ " is an illegal signature." ) ) ;
2022-11-13 23:17:18 +08:00
}
catch ( Exception ex )
{
2024-02-05 15:58:42 +08:00
result = ErroredResult . Fail ( ex ) ;
2022-11-13 23:17:18 +08:00
}
}
2024-02-05 15:58:42 +08:00
break ;
2022-11-13 23:17:18 +08:00
2024-02-06 11:23:04 +08:00
case SignSchemes . WECHATPAY2_SM2_WITH_SM3 :
2022-05-09 19:28:47 +08:00
{
2024-02-05 15:58:42 +08:00
try
2022-05-09 19:28:47 +08:00
{
2024-11-05 21:19:50 +08:00
bool valid = Utilities . SM2Utility . VerifyWithSM3 (
publicKeyPem : publicKey ,
2024-02-06 11:23:04 +08:00
messageData : message ,
encodingSignature : new EncodedString ( signature , EncodingKinds . Base64 )
2022-11-13 23:17:18 +08:00
) ;
2024-02-05 15:58:42 +08:00
if ( valid )
result = ErroredResult . Ok ( ) ;
else
2024-02-06 11:23:04 +08:00
result = ErroredResult . Fail ( new Exception ( $"Signature does not match. Maybe \" { signature } \ " is an illegal signature." ) ) ;
2022-11-13 23:17:18 +08:00
}
catch ( Exception ex )
{
2024-02-05 15:58:42 +08:00
result = ErroredResult . Fail ( ex ) ;
2022-11-13 23:17:18 +08:00
}
2022-05-09 19:28:47 +08:00
}
2024-02-05 15:58:42 +08:00
break ;
2022-05-09 19:28:47 +08:00
default :
{
2024-02-06 11:23:04 +08:00
result = ErroredResult . Fail ( new Exception ( $"Unsupported signing scheme: \" { scheme } \ "." ) ) ;
2022-05-09 19:28:47 +08:00
}
2024-02-05 15:58:42 +08:00
break ;
2022-05-09 19:28:47 +08:00
}
2024-02-05 15:58:42 +08:00
return result ;
2022-05-09 19:28:47 +08:00
}
}
}