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

This commit is contained in:
Fu Diwei
2024-02-05 13:54:36 +08:00
committed by RHQYZ
parent 7d565811a1
commit 1c42f912ef
3 changed files with 124 additions and 281 deletions

View File

@@ -2,6 +2,8 @@ using System;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
{
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayBusinessClientEventVerificationExtensions
{
/// <summary>
@@ -11,84 +13,14 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <param name="webhookAuthorization"></param>
/// <param name="webhookBody"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookAuthorization, string webhookBody)
{
return VerifyEventSignature(client, webhookAuthorization, webhookBody, out _);
}
/// <summary>
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="webhookAuthorization"></param>
/// <param name="webhookBody"></param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookAuthorization, string webhookBody, out Exception? error)
public static ErroredResult VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookAuthorization, string webhookBody)
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (webhookAuthorization is null) throw new ArgumentNullException(nameof(webhookAuthorization));
if (webhookBody is null) throw new ArgumentNullException(nameof(webhookBody));
bool ret = WechatTenpayBusinessClientSigningExtensions.VerifySignature(
return WechatTenpayBusinessClientSigningExtensions.VerifySignature(
client,
strAuthorization: webhookAuthorization,
strContent: webhookBody,
out error
);
if (!ret)
error ??= new Exception($"Failed to verify webhook event. Maybe the raw signature is invalid.");
return ret;
}
/// <summary>
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="webhookTimestamp"></param>
/// <param name="webhookNonce">。</param>
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSerialNumber"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSerialNumber)
{
return VerifyEventSignature(
client,
webhookTimestamp: webhookTimestamp,
webhookNonce: webhookNonce,
webhookBody: webhookBody,
webhookSignature: webhookSignature,
webhookSignatureAlgorithm: Constants.SignAlgorithms.SHA256_WITH_RSA,
webhookSerialNumber: webhookSerialNumber,
out _
);
}
/// <summary>
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="webhookTimestamp"></param>
/// <param name="webhookNonce">。</param>
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSignatureAlgorithm"></param>
/// <param name="webhookSerialNumber"></param>
/// <returns></returns>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSignatureAlgorithm, string webhookSerialNumber)
{
return VerifyEventSignature(
client,
webhookTimestamp: webhookTimestamp,
webhookNonce: webhookNonce,
webhookBody: webhookBody,
webhookSignature: webhookSignature,
webhookSignatureAlgorithm: webhookSignatureAlgorithm,
webhookSerialNumber: webhookSerialNumber,
out _
strContent: webhookBody
);
}
@@ -101,55 +33,46 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSerialNumber"></param>
/// <param name="error"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient 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,
webhookSignatureAlgorithm: Constants.SignAlgorithms.SHA256_WITH_RSA,
webhookSerialNumber: webhookSerialNumber,
out error
);
}
/// <summary>
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="webhookTimestamp"></param>
/// <param name="webhookNonce">。</param>
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSignatureAlgorithm"></param>
/// <param name="webhookSerialNumber"></param>
/// <param name="error"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static bool VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSignatureAlgorithm, string webhookSerialNumber, out Exception? error)
public static ErroredResult VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSerialNumber)
{
if (client is null) throw new ArgumentNullException(nameof(client));
bool ret = WechatTenpayBusinessClientSigningExtensions.VerifySignature(
return VerifyEventSignature(
client,
webhookTimestamp: webhookTimestamp,
webhookNonce: webhookNonce,
webhookBody: webhookBody,
webhookSignature: webhookSignature,
webhookSignatureAlgorithm: Constants.SignAlgorithms.SHA256_WITH_RSA,
webhookSerialNumber: webhookSerialNumber
);
}
/// <summary>
/// <para>验证回调通知事件签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="webhookTimestamp"></param>
/// <param name="webhookNonce">。</param>
/// <param name="webhookBody"></param>
/// <param name="webhookSignature"></param>
/// <param name="webhookSignatureAlgorithm"></param>
/// <param name="webhookSerialNumber"></param>
/// <returns></returns>
public static ErroredResult VerifyEventSignature(this WechatTenpayBusinessClient client, string webhookTimestamp, string webhookNonce, string webhookBody, string webhookSignature, string webhookSignatureAlgorithm, string webhookSerialNumber)
{
if (client is null) throw new ArgumentNullException(nameof(client));
return WechatTenpayBusinessClientSigningExtensions.VerifySignature(
client,
strTimestamp: webhookTimestamp,
strNonce: webhookNonce,
strContent: webhookBody,
strSignature: webhookSignature,
strSignatureAlgorithm: webhookSignatureAlgorithm,
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.TenpayBusiness
{
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayBusinessClientResponseVerificationExtensions
{
/// <summary>
@@ -12,86 +14,15 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <param name="client"></param>
/// <param name="response"></param>
/// <returns></returns>
public static bool VerifyResponseSignature<TResponse>(this WechatTenpayBusinessClient client, TResponse response)
where TResponse : WechatTenpayBusinessResponse
{
return VerifyResponseSignature(client, response, out _);
}
/// <summary>
/// <para>验证响应签名。</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 WechatTenpayBusinessClient client, TResponse response, out Exception? error)
public static ErroredResult VerifyResponseSignature<TResponse>(this WechatTenpayBusinessClient client, TResponse response)
where TResponse : WechatTenpayBusinessResponse
{
if (client is null) throw new ArgumentNullException(nameof(client));
bool ret = WechatTenpayBusinessClientSigningExtensions.VerifySignature(
return WechatTenpayBusinessClientSigningExtensions.VerifySignature(
client,
strAuthorization: response.GetRawHeaders().GetFirstValueOrEmpty("TBEP-Authorization"),
strContent: Encoding.UTF8.GetString(response.GetRawBytes()),
out error
);
if (error is not null)
{
error = new WechatTenpayBusinessException("Verify signature of response failed. Please see the inner exception for more details.", error);
}
return ret;
}
/// <summary>
/// <para>验证响应签名。</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>
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayBusinessClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSerialNumber)
{
return VerifyResponseSignature(
client,
responseTimestamp: responseTimestamp,
responseNonce: responseNonce,
responseBody: responseBody,
responseSignature: responseSignature,
responseSignatureAlgorithm: Constants.SignAlgorithms.SHA256_WITH_RSA,
responseSerialNumber: responseSerialNumber,
out _
);
}
/// <summary>
/// <para>验证响应签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="responseTimestamp"></param>
/// <param name="responseNonce">。</param>
/// <param name="responseBody"></param>
/// <param name="responseSignature"></param>
/// <param name="responseSignatureAlgorithm"></param>
/// <param name="responseSerialNumber"></param>
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayBusinessClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSignatureAlgorithm, string responseSerialNumber)
{
return VerifyResponseSignature(
client,
responseTimestamp: responseTimestamp,
responseNonce: responseNonce,
responseBody: responseBody,
responseSignature: responseSignature,
responseSignatureAlgorithm: responseSignatureAlgorithm,
responseSerialNumber: responseSerialNumber,
out _
strContent: Encoding.UTF8.GetString(response.GetRawBytes())
);
}
@@ -104,53 +35,46 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
/// <param name="responseBody"></param>
/// <param name="responseSignature"></param>
/// <param name="responseSerialNumber"></param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayBusinessClient 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,
responseSignatureAlgorithm: Constants.SignAlgorithms.SHA256_WITH_RSA,
responseSerialNumber: responseSerialNumber,
out error
);
}
/// <summary>
/// <para>验证响应签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="responseTimestamp"></param>
/// <param name="responseNonce">。</param>
/// <param name="responseBody"></param>
/// <param name="responseSignature"></param>
/// <param name="responseSignatureAlgorithm"></param>
/// <param name="responseSerialNumber"></param>
/// <param name="error"></param>
/// <returns></returns>
public static bool VerifyResponseSignature(this WechatTenpayBusinessClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSignatureAlgorithm, string responseSerialNumber, out Exception? error)
public static ErroredResult VerifyResponseSignature(this WechatTenpayBusinessClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSerialNumber)
{
if (client is null) throw new ArgumentNullException(nameof(client));
bool ret = WechatTenpayBusinessClientSigningExtensions.VerifySignature(
return VerifyResponseSignature(
client,
responseTimestamp: responseTimestamp,
responseNonce: responseNonce,
responseBody: responseBody,
responseSignature: responseSignature,
responseSignatureAlgorithm: Constants.SignAlgorithms.SHA256_WITH_RSA,
responseSerialNumber: responseSerialNumber
);
}
/// <summary>
/// <para>验证响应签名。</para>
/// </summary>
/// <param name="client"></param>
/// <param name="responseTimestamp"></param>
/// <param name="responseNonce">。</param>
/// <param name="responseBody"></param>
/// <param name="responseSignature"></param>
/// <param name="responseSignatureAlgorithm"></param>
/// <param name="responseSerialNumber"></param>
/// <returns></returns>
public static ErroredResult VerifyResponseSignature(this WechatTenpayBusinessClient client, string responseTimestamp, string responseNonce, string responseBody, string responseSignature, string responseSignatureAlgorithm, string responseSerialNumber)
{
if (client is null) throw new ArgumentNullException(nameof(client));
return WechatTenpayBusinessClientSigningExtensions.VerifySignature(
client,
strTimestamp: responseTimestamp,
strNonce: responseNonce,
strContent: responseBody,
strSignature: responseSignature,
strSignatureAlgorithm: responseSignatureAlgorithm,
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

@@ -8,90 +8,86 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
internal static class WechatTenpayBusinessClientSigningExtensions
{
public static bool VerifySignature(this WechatTenpayBusinessClient client, string strAuthorization, string strContent, out Exception? error)
public static ErroredResult VerifySignature(this WechatTenpayBusinessClient client, string strAuthorization, string strContent)
{
if (!string.IsNullOrEmpty(strAuthorization))
{
try
{
IDictionary<string, string?> dictAuthorization = strAuthorization
.Split(',')
.Select(s => s.Trim().Split(new char[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(
k => k[0],
v => v.Length > 1 ? v[1].TrimStart('\"').TrimEnd('\"') : null
);
string strTimestamp = dictAuthorization.GetValueOrDefault("timestamp")!;
string strNonce = dictAuthorization.GetValueOrDefault("nonce")!;
string strSignature = dictAuthorization.GetValueOrDefault("signature")!;
string strSignAlgorithm = dictAuthorization.GetValueOrDefault("signature_algorithm")!;
string strSerialNumber = dictAuthorization.GetValueOrDefault("tbep_serial_number")!;
ErroredResult result;
return VerifySignature(
client,
strTimestamp: strTimestamp,
strNonce: strNonce,
strContent: strContent,
strSignature: strSignature,
strSignatureAlgorithm: strSignAlgorithm,
strSerialNumber: strSerialNumber,
out error
try
{
IDictionary<string, string?> dictAuthorization = strAuthorization
.Split(',')
.Select(s => s.Trim().Split(new char[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(
k => k[0],
v => v.Length > 1 ? v[1].TrimStart('\"').TrimEnd('\"') : null
);
}
catch (Exception ex)
{
error = ex;
return false;
}
string strTimestamp = dictAuthorization.GetValueOrDefault("timestamp")!;
string strNonce = dictAuthorization.GetValueOrDefault("nonce")!;
string strSignature = dictAuthorization.GetValueOrDefault("signature")!;
string strSignAlgorithm = dictAuthorization.GetValueOrDefault("signature_algorithm")!;
string strSerialNumber = dictAuthorization.GetValueOrDefault("tbep_serial_number")!;
result = VerifySignature(
client,
strTimestamp: strTimestamp,
strNonce: strNonce,
strContent: strContent,
strSignature: strSignature,
strSignatureAlgorithm: strSignAlgorithm,
strSerialNumber: strSerialNumber
);
}
catch (Exception ex)
{
result = ErroredResult.Fail(ex);
}
error = new Exception("The value of \"TBEP-Authorization\" is empty.");
return false;
return result;
}
public static bool VerifySignature(this WechatTenpayBusinessClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignatureAlgorithm, string strSerialNumber, out Exception? error)
public static ErroredResult VerifySignature(this WechatTenpayBusinessClient client, string strTimestamp, string strNonce, string strContent, string strSignature, string strSignatureAlgorithm, string strSerialNumber)
{
if (client is null) throw new ArgumentNullException(nameof(client));
ErroredResult result;
switch (strSignatureAlgorithm)
{
case Constants.SignAlgorithms.SHA256_WITH_RSA:
{
if (client.Credentials.TBEPCertificateSerialNumber is not null &&
client.Credentials.TBEPCertificatePublicKey is not null)
try
{
try
if (!string.Equals(client.Credentials.TBEPCertificateSerialNumber, strSerialNumber))
{
if (!string.Equals(client.Credentials.TBEPCertificateSerialNumber, strSerialNumber))
{
error = new Exception($"There is no TBEP public key matched the serial number \"{strSerialNumber}\".");
return false;
}
result = ErroredResult.Fail(new Exception($"There is no TBEP public key matched the serial number \"{strSerialNumber}\"."));
break;
}
error = null;
return Utilities.RSAUtility.Verify(
publicKeyPem: client.Credentials.TBEPCertificatePublicKey,
message: GenerateMessageForSignature(timestamp: strTimestamp, nonce: strNonce, body: strContent),
encodingSignature: new EncodedString(strSignature, EncodingKinds.Base64)
);
}
catch (Exception ex)
{
error = ex;
return false;
}
bool valid = Utilities.RSAUtility.Verify(
publicKeyPem: client.Credentials.TBEPCertificatePublicKey,
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)
{
result = ErroredResult.Fail(ex);
}
error = new Exception("There is no TBEP public key or serial number.");
return false;
}
break;
default:
{
error = new Exception($"Unsupported sign algorithm: \"{strSignatureAlgorithm}\".");
return false;
result = ErroredResult.Fail(new Exception($"Unsupported sign algorithm: \"{strSignatureAlgorithm}\"."));
}
break;
}
return result;
}
private static string GenerateMessageForSignature(string timestamp, string nonce, string body)