feat(tenpayv3): 基于 ErroredResult 改造验签相关扩展方法

This commit is contained in:
Fu Diwei 2024-02-05 15:58:42 +08:00 committed by RHQYZ
parent 1c42f912ef
commit c631ece274
3 changed files with 62 additions and 199 deletions

View File

@ -2,6 +2,8 @@ using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayClientEventVerificationExtensions
{
/// <summary>
@ -16,7 +18,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
/// <param name="webhookSignature">微信回调通知中的 "Wechatpay-Signature" 请求标头。</param>
/// <param name="webhookSerialNumber">微信回调通知中的 "Wechatpay-Serial" 请求标头。</param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSerialNumber)
public static ErroredResult VerifyEventSignature(this WechatTenpayClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSerialNumber)
{
return VerifyEventSignature(
client,
@ -42,80 +44,19 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
/// <param name="webhookSignatureType">微信回调通知中的 "Wechatpay-Signature-Type" 请求标头。</param>
/// <param name="webhookSerialNumber">微信回调通知中的 "Wechatpay-Serial" 请求标头。</param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSignatureType, string webhookSerialNumber)
{
return VerifyEventSignature(
client,
webhookTimestamp: webhookTimestamp,
webhookNonce: webhookNonce,
webhookBody: webhookBody,
webhookSignature: webhookSignature,
webhookSignatureType: webhookSignatureType,
webhookSerialNumber: webhookSerialNumber,
out _
);
}
/// <summary>
/// <para>验证回调通知事件签名。</para>
/// <para>REF: https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html </para>
/// <para>REF: https://pay.weixin.qq.com/docs/partner/development/interface-rules/signature-verification.html </para>
/// </summary>
/// <param name="client"></param>
/// <param name="webhookTimestamp">微信回调通知中的 "Wechatpay-Timestamp" 请求标头。</param>
/// <param name="webhookNonce">微信回调通知中的 "Wechatpay-Nonce" 请求标头。</param>
/// <param name="webhookBody">微信回调通知中请求正文。</param>
/// <param name="webhookSignature">微信回调通知中的 "Wechatpay-Signature" 请求标头。</param>
/// <param name="webhookSerialNumber">微信回调通知中的 "Wechatpay-Serial" 请求标头。</param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSerialNumber, out Exception? error)
{
return VerifyEventSignature(
client,
webhookTimestamp: webhookTimestamp,
webhookNonce: webhookNonce,
webhookBody: webhookBody,
webhookSignature: webhookSignature,
webhookSignatureType: Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256,
webhookSerialNumber: webhookSerialNumber,
out error
);
}
/// <summary>
/// <para>验证回调通知事件签名。</para>
/// <para>REF: https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html </para>
/// <para>REF: https://pay.weixin.qq.com/docs/partner/development/interface-rules/signature-verification.html </para>
/// </summary>
/// <param name="client"></param>
/// <param name="webhookTimestamp">微信回调通知中的 "Wechatpay-Timestamp" 请求标头。</param>
/// <param name="webhookNonce">微信回调通知中的 "Wechatpay-Nonce" 请求标头。</param>
/// <param name="webhookBody">微信回调通知中请求正文。</param>
/// <param name="webhookSignature">微信回调通知中的 "Wechatpay-Signature" 请求标头。</param>
/// <param name="webhookSignatureType">微信回调通知中的 "Wechatpay-Signature-Type" 请求标头。</param>
/// <param name="webhookSerialNumber">微信回调通知中的 "Wechatpay-Serial" 请求标头。</param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSignatureType, string webhookSerialNumber, out Exception? error)
public static ErroredResult VerifyEventSignature(this WechatTenpayClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSignatureType, string webhookSerialNumber)
{
if (client is null) throw new ArgumentNullException(nameof(client));
bool ret = WechatTenpayClientSigningExtensions.VerifySignature(
return WechatTenpayClientSigningExtensions.VerifySignature(
client,
strTimestamp: webhookTimestamp,
strNonce: webhookNonce,
strContent: webhookBody,
strSignature: webhookSignature,
strSignScheme: webhookSignatureType,
strSerialNumber: webhookSerialNumber,
out error
strSerialNumber: webhookSerialNumber
);
if (!ret)
error ??= new Exception($"Failed to verify webhook event. Maybe the raw signature \"{webhookSignature}\" is invalid.");
return ret;
}
}
}

View File

@ -3,6 +3,8 @@ using System.Text;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayClientResponseVerificationExtensions
{
/// <summary>
@ -14,23 +16,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
/// <param name="client"></param>
/// <param name="response"></param>
/// <returns></returns>
public static bool VerifyResponseSignature<TResponse>(this WechatTenpayClient client, TResponse response)
where TResponse : WechatTenpayResponse
{
return VerifyResponseSignature(client, response, out _);
}
/// <summary>
/// <para>验证响应签名。</para>
/// <para>REF: https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html </para>
/// <para>REF: https://pay.weixin.qq.com/docs/partner/development/interface-rules/signature-verification.html </para>
/// </summary>
/// <typeparam name="TResponse"></typeparam>
/// <param name="client"></param>
/// <param name="response"></param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyResponseSignature<TResponse>(this WechatTenpayClient client, TResponse response, out Exception? error)
public static ErroredResult VerifyResponseSignature<TResponse>(this WechatTenpayClient client, TResponse response)
where TResponse : WechatTenpayResponse
{
if (client is null) throw new ArgumentNullException(nameof(client));
@ -43,8 +29,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
responseBody: Encoding.UTF8.GetString(response.GetRawBytes()),
responseSignature: response.WechatpaySignature,
responseSignatureType: response.WechatpaySignatureType,
responseSerialNumber: response.WechatpayCertificateSerialNumber,
out error
responseSerialNumber: response.WechatpayCertificateSerialNumber
);
}
@ -60,7 +45,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
/// <param name="responseSignature"></param>
/// <param name="responseSerialNumber"></param>
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSerialNumber)
public static ErroredResult VerifyResponseSignature(this WechatTenpayClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSerialNumber)
{
return VerifyResponseSignature(
client,
@ -69,7 +54,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
responseBody: responseBody,
responseSignature: responseSignature,
responseSignatureType: Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256,
responseSerialNumber: responseSerialNumber
responseSerialNumber
);
}
@ -86,80 +71,19 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
/// <param name="responseSignatureType"></param>
/// <param name="responseSerialNumber"></param>
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSignatureType, string responseSerialNumber)
{
return VerifyResponseSignature(
client,
responseTimestamp: responseTimestamp,
responseNonce: responseNonce,
responseBody: responseBody,
responseSignature: responseSignature,
responseSignatureType: responseSignatureType,
responseSerialNumber,
out _
);
}
/// <summary>
/// <para>验证响应签名。</para>
/// <para>REF: https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html </para>
/// <para>REF: https://pay.weixin.qq.com/docs/partner/development/interface-rules/signature-verification.html </para>
/// </summary>
/// <param name="client"></param>
/// <param name="responseTimestamp"></param>
/// <param name="responseNonce">。</param>
/// <param name="responseBody"></param>
/// <param name="responseSignature"></param>
/// <param name="responseSerialNumber"></param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSerialNumber, out Exception? error)
{
return VerifyResponseSignature(
client,
responseTimestamp: responseTimestamp,
responseNonce: responseNonce,
responseBody: responseBody,
responseSignature: responseSignature,
responseSignatureType: Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256,
responseSerialNumber,
out error
);
}
/// <summary>
/// <para>验证响应签名。</para>
/// <para>REF: https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html </para>
/// <para>REF: https://pay.weixin.qq.com/docs/partner/development/interface-rules/signature-verification.html </para>
/// </summary>
/// <param name="client"></param>
/// <param name="responseTimestamp"></param>
/// <param name="responseNonce">。</param>
/// <param name="responseBody"></param>
/// <param name="responseSignature"></param>
/// <param name="responseSignatureType"></param>
/// <param name="responseSerialNumber"></param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSignatureType, string responseSerialNumber, out Exception? error)
public static ErroredResult VerifyResponseSignature(this WechatTenpayClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSignatureType, string responseSerialNumber)
{
if (client is null) throw new ArgumentNullException(nameof(client));
bool ret = WechatTenpayClientSigningExtensions.VerifySignature(
return WechatTenpayClientSigningExtensions.VerifySignature(
client,
strTimestamp: responseTimestamp,
strNonce: responseNonce,
strContent: responseBody,
strSignature: responseSignature,
strSignScheme: responseSignatureType,
strSerialNumber: responseSerialNumber,
out error
strSerialNumber: responseSerialNumber
);
if (!ret)
error ??= new Exception($"Failed to verify response. Maybe the raw signature \"{responseSignature}\" is invalid.");
return ret;
}
}
}

View File

@ -7,92 +7,90 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
internal static class WechatTenpayClientSigningExtensions
{
public static bool VerifySignature(this WechatTenpayClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber, out Exception? error)
public static ErroredResult VerifySignature(this WechatTenpayClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignScheme, string strSerialNumber)
{
if (client is null) throw new ArgumentNullException(nameof(client));
ErroredResult result;
switch (strSignScheme)
{
case Constants.SignSchemes.WECHATPAY2_RSA_2048_WITH_SHA256:
{
if (client.PlatformCertificateManager is null)
{
error = new Exception("The platform certificate manager is not initialized.");
return false;
}
CertificateEntry? entry = client.PlatformCertificateManager.GetEntry(strSerialNumber);
if (!entry.HasValue)
{
error = new Exception($"The platform certificate manager does not contain a certificate with serial number \"{strSerialNumber}\".");
return false;
}
if (!CertificateEntry.ALGORITHM_TYPE_RSA.Equals(entry.Value.AlgorithmType))
{
error = new Exception($"The platform certificate with serial number \"{strSerialNumber}\" is not for RSA.");
return false;
}
error = null;
try
{
return Utilities.RSAUtility.VerifyByCertificate(
if (client.PlatformCertificateManager is null)
{
result = ErroredResult.Fail(new Exception("The platform certificate manager is not initialized."));
break;
}
CertificateEntry? entry = client.PlatformCertificateManager.GetEntry(strSerialNumber);
if (!entry.HasValue)
{
result = ErroredResult.Fail(new Exception($"The platform certificate manager does not contain a certificate with serial number \"{strSerialNumber}\"."));
break;
}
bool valid = Utilities.RSAUtility.VerifyByCertificate(
certificatePem: entry.Value.Certificate,
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
encodingSignature: new EncodedString(strSignature, EncodingKinds.Base64)
);
if (valid)
result = ErroredResult.Ok();
else
result = ErroredResult.Fail(new Exception($"Signature does not match. Maybe \"{strSignature}\" is an illegal signature."));
}
catch (Exception ex)
{
error = ex;
return false;
result = ErroredResult.Fail(ex);
}
}
break;
case Constants.SignSchemes.WECHATPAY2_SM2_WITH_SM3:
{
if (client.PlatformCertificateManager is null)
{
error = new Exception("The platform certificate manager is not initialized.");
return false;
}
CertificateEntry? entry = client.PlatformCertificateManager.GetEntry(strSerialNumber);
if (!entry.HasValue)
{
error = new Exception($"The platform certificate manager does not contain a certificate with serial number \"{strSerialNumber}\".");
return false;
}
if (!CertificateEntry.ALGORITHM_TYPE_SM2.Equals(entry.Value.AlgorithmType))
{
error = new Exception($"The platform certificate with serial number \"{strSerialNumber}\" is not for SM2.");
return false;
}
error = null;
try
{
return Utilities.SM2Utility.VerifyWithSM3ByCertificate(
if (client.PlatformCertificateManager is null)
{
result = ErroredResult.Fail(new Exception("The platform certificate manager is not initialized."));
break;
}
CertificateEntry? entry = client.PlatformCertificateManager.GetEntry(strSerialNumber);
if (!entry.HasValue)
{
result = ErroredResult.Fail(new Exception($"The platform certificate manager does not contain a certificate with serial number \"{strSerialNumber}\"."));
break;
}
bool valid = Utilities.SM2Utility.VerifyWithSM3ByCertificate(
certificatePem: entry.Value.Certificate,
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
encodingSignature: new EncodedString(strSignature, EncodingKinds.Base64)
);
if (valid)
result = ErroredResult.Ok();
else
result = ErroredResult.Fail(new Exception($"Signature does not match. Maybe \"{strSignature}\" is an illegal signature."));
}
catch (Exception ex)
{
error = ex;
return false;
result = ErroredResult.Fail(ex);
}
}
break;
default:
{
error = new Exception($"Unsupported signing scheme: \"{strSignScheme}\".");
return false;
result = ErroredResult.Fail(new Exception($"Unsupported signing scheme: \"{strSignScheme}\"."));
}
break;
}
return result;
}
private static string GenerateMessageForSignature(string timestamp, string nonce, string body)