mirror of
https://gitee.com/fudiwei/DotNetCore.SKIT.FlurlHttpClient.Wechat.git
synced 2026-03-10 00:13:36 +08:00
refactor(tenpayv2): 优化加解密及哈希算法工具类
This commit is contained in:
+6
-5
@@ -1,8 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2
|
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2
|
||||||
{
|
{
|
||||||
|
using SKIT.FlurlHttpClient.Primitives;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 为 <see cref="WechatTenpayClient"/> 提供回调通知事件敏感数据解密的扩展方法。
|
/// 为 <see cref="WechatTenpayClient"/> 提供回调通知事件敏感数据解密的扩展方法。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,11 +57,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string key = Utilities.MD5Utility.Hash(client.Credentials.MerchantSecret).ToLower();
|
string key = Utilities.MD5Utility.Hash(client.Credentials.MerchantSecret).Value!.ToLower();
|
||||||
string plainXml = Utilities.AESUtility.DecryptWithECB(
|
string plainXml = Utilities.AESUtility.DecryptWithECB(
|
||||||
encodingKey: Convert.ToBase64String(Encoding.UTF8.GetBytes(key)),
|
encodingKey: new EncodedString(key, EncodingKinds.Literal),
|
||||||
encodingCipherText: webhookEvent.EncryptedRequestInfo!
|
encodingCipher: new EncodedString(webhookEvent.EncryptedRequestInfo!, EncodingKinds.Base64)
|
||||||
);
|
)!;
|
||||||
plainJson = Utilities.XmlHelper.ConvertToJson(plainXml);
|
plainJson = Utilities.XmlHelper.ConvertToJson(plainXml);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
+3
-1
@@ -10,6 +10,8 @@ using Flurl.Http;
|
|||||||
|
|
||||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2
|
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2
|
||||||
{
|
{
|
||||||
|
using SKIT.FlurlHttpClient.Primitives;
|
||||||
|
|
||||||
public static class WechatTenpayClientExecuteMerchantMediaExtensions
|
public static class WechatTenpayClientExecuteMerchantMediaExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -29,7 +31,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2
|
|||||||
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".jpg";
|
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".jpg";
|
||||||
|
|
||||||
if (request.FileHash is null)
|
if (request.FileHash is null)
|
||||||
request.FileHash = BitConverter.ToString(Utilities.MD5Utility.Hash(request.FileBytes ?? Array.Empty<byte>())).Replace("-", string.Empty).ToLower();
|
request.FileHash = EncodedString.ToHexString(Utilities.MD5Utility.Hash(request.FileBytes ?? Array.Empty<byte>())).Value!.ToLower();
|
||||||
|
|
||||||
IFlurlRequest flurlReq = client
|
IFlurlRequest flurlReq = client
|
||||||
.CreateFlurlRequest(request, HttpMethod.Post, "secapi", "mch", "uploadmedia");
|
.CreateFlurlRequest(request, HttpMethod.Post, "secapi", "mch", "uploadmedia");
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Models
|
|||||||
[System.Text.Json.Serialization.JsonConverter(typeof(Converters.ResponseClassSystemTextJsonConverter))]
|
[System.Text.Json.Serialization.JsonConverter(typeof(Converters.ResponseClassSystemTextJsonConverter))]
|
||||||
public class GetTransitPartnerOrderResponse : WechatTenpaySignableResponse
|
public class GetTransitPartnerOrderResponse : WechatTenpaySignableResponse
|
||||||
{
|
{
|
||||||
public static new class Types
|
public static class Types
|
||||||
{
|
{
|
||||||
public class Coupon : GetTransitOrderResponse.Types.Coupon
|
public class Coupon : GetTransitOrderResponse.Types.Coupon
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
||||||
{
|
{
|
||||||
|
using SKIT.FlurlHttpClient.Primitives;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AES 算法工具类。
|
/// AES 算法工具类。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -20,8 +21,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
|||||||
if (keyBytes is null) throw new ArgumentNullException(nameof(keyBytes));
|
if (keyBytes is null) throw new ArgumentNullException(nameof(keyBytes));
|
||||||
if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes));
|
if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes));
|
||||||
|
|
||||||
using (SymmetricAlgorithm aes = Aes.Create())
|
using SymmetricAlgorithm aes = Aes.Create();
|
||||||
{
|
|
||||||
aes.Mode = CipherMode.ECB;
|
aes.Mode = CipherMode.ECB;
|
||||||
aes.Padding = PaddingMode.PKCS7;
|
aes.Padding = PaddingMode.PKCS7;
|
||||||
aes.Key = keyBytes;
|
aes.Key = keyBytes;
|
||||||
@@ -29,24 +29,23 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
|||||||
using ICryptoTransform transform = aes.CreateDecryptor();
|
using ICryptoTransform transform = aes.CreateDecryptor();
|
||||||
return transform.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
|
return transform.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 基于 ECB 模式解密数据。
|
/// 基于 ECB 模式解密数据。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="encodingKey">经 Base64 编码后的 AES 密钥。</param>
|
/// <param name="encodingKey">经过编码后的(通常为 Base64)AES 密钥。</param>
|
||||||
/// <param name="encodingCipherText">经 Base64 编码后的待解密数据。</param>
|
/// <param name="encodingCipher">经过编码后的(通常为 Base64)待解密数据。</param>
|
||||||
/// <returns>解密后的文本数据。</returns>
|
/// <returns>解密后的数据。</returns>
|
||||||
public static string DecryptWithECB(string encodingKey, string encodingCipherText)
|
public static EncodedString DecryptWithECB(EncodedString encodingKey, EncodedString encodingCipher)
|
||||||
{
|
{
|
||||||
if (encodingKey is null) throw new ArgumentNullException(nameof(encodingKey));
|
if (encodingKey.Value is null) throw new ArgumentNullException(nameof(encodingKey));
|
||||||
if (encodingCipherText is null) throw new ArgumentNullException(nameof(encodingCipherText));
|
if (encodingCipher.Value is null) throw new ArgumentNullException(nameof(encodingCipher));
|
||||||
|
|
||||||
byte[] plainBytes = DecryptWithECB(
|
byte[] plainBytes = DecryptWithECB(
|
||||||
keyBytes: Convert.FromBase64String(encodingKey),
|
keyBytes: EncodedString.FromString(encodingKey, fallbackEncodingKind: EncodingKinds.Base64),
|
||||||
cipherBytes: Convert.FromBase64String(encodingCipherText)
|
cipherBytes: EncodedString.FromString(encodingCipher, fallbackEncodingKind: EncodingKinds.Base64)
|
||||||
);
|
);
|
||||||
return Encoding.UTF8.GetString(plainBytes);
|
return EncodedString.ToLiteralString(plainBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,49 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
||||||
{
|
{
|
||||||
|
using SKIT.FlurlHttpClient.Primitives;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HMAC 算法工具类。
|
/// HMAC 算法工具类。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class HMACUtility
|
public static class HMACUtility
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 HMAC-SHA-256 消息认证码。
|
/// 计算 HMAC-SHA-256 哈希值。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="secretBytes">密钥字节数组。</param>
|
/// <param name="keyBytes">密钥字节数组。</param>
|
||||||
/// <param name="msgBytes">信息字节数组。</param>
|
/// <param name="msgBytes">要计算哈希值的信息字节数组。</param>
|
||||||
/// <returns>消息认证码字节数组。</returns>
|
/// <returns>哈希值字节数组。</returns>
|
||||||
public static byte[] HashWithSHA256(byte[] secretBytes, byte[] msgBytes)
|
public static byte[] HashWithSHA256(byte[] keyBytes, byte[] msgBytes)
|
||||||
{
|
{
|
||||||
if (secretBytes is null) throw new ArgumentNullException(nameof(secretBytes));
|
if (keyBytes is null) throw new ArgumentNullException(nameof(keyBytes));
|
||||||
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
if (msgBytes is null) throw new ArgumentNullException(nameof(msgBytes));
|
||||||
|
|
||||||
using HMAC hmac = new HMACSHA256(secretBytes);
|
#if NET5_0_OR_GREATER
|
||||||
|
return HMACSHA256.HashData(keyBytes, msgBytes);
|
||||||
|
#else
|
||||||
|
using HMAC hmac = new HMACSHA256(keyBytes);
|
||||||
return hmac.ComputeHash(msgBytes);
|
return hmac.ComputeHash(msgBytes);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 HMAC-SHA-256 消息认证码。
|
/// 计算 HMAC-SHA-256 哈希值。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="secret">密钥。</param>
|
/// <param name="key">密钥。</param>
|
||||||
/// <param name="message">文本信息。</param>
|
/// <param name="message">要计算哈希值的信息。</param>
|
||||||
/// <returns>消息认证码。</returns>
|
/// <returns>经过十六进制编码的哈希值。</returns>
|
||||||
public static string HashWithSHA256(string secret, string message)
|
public static EncodedString HashWithSHA256(string key, string message)
|
||||||
{
|
{
|
||||||
if (secret is null) throw new ArgumentNullException(nameof(secret));
|
if (key is null) throw new ArgumentNullException(nameof(key));
|
||||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||||
|
|
||||||
byte[] secretBytes = Encoding.UTF8.GetBytes(secret);
|
byte[] keyBytes = EncodedString.FromLiteralString(key);
|
||||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
byte[] msgBytes = EncodedString.FromLiteralString(message);
|
||||||
byte[] hashBytes = HashWithSHA256(secretBytes, msgBytes);
|
byte[] hashBytes = HashWithSHA256(keyBytes, msgBytes);
|
||||||
return BitConverter.ToString(hashBytes).Replace("-", string.Empty);
|
return EncodedString.ToHexString(hashBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,44 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
||||||
{
|
{
|
||||||
|
using SKIT.FlurlHttpClient.Primitives;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MD5 算法工具类。
|
/// MD5 算法工具类。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class MD5Utility
|
public static class MD5Utility
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 MD5 信息摘要。
|
/// 计算 MD5 哈希值。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bytes">信息字节数组。</param>
|
/// <param name="bytes">要计算哈希值的信息字节数组。</param>
|
||||||
/// <returns>信息摘要字节数组。</returns>
|
/// <returns>哈希值字节数组。</returns>
|
||||||
public static byte[] Hash(byte[] bytes)
|
public static byte[] Hash(byte[] bytes)
|
||||||
{
|
{
|
||||||
if (bytes is null) throw new ArgumentNullException(nameof(bytes));
|
if (bytes is null) throw new ArgumentNullException(nameof(bytes));
|
||||||
|
|
||||||
|
#if NET5_0_OR_GREATER
|
||||||
|
return MD5.HashData(bytes);
|
||||||
|
#else
|
||||||
using MD5 md5 = MD5.Create();
|
using MD5 md5 = MD5.Create();
|
||||||
return md5.ComputeHash(bytes);
|
return md5.ComputeHash(bytes);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 MD5 信息摘要。
|
/// 计算 MD5 哈希值。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">文本信息。</param>
|
/// <param name="message">要计算哈希值的信息。</param>
|
||||||
/// <returns>信息摘要。</returns>
|
/// <returns>经过十六进制编码的哈希值。</returns>
|
||||||
public static string Hash(string message)
|
public static EncodedString Hash(string message)
|
||||||
{
|
{
|
||||||
if (message is null) throw new ArgumentNullException(nameof(message));
|
if (message is null) throw new ArgumentNullException(nameof(message));
|
||||||
|
|
||||||
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
|
byte[] msgBytes = EncodedString.FromLiteralString(message);
|
||||||
byte[] hashBytes = Hash(msgBytes);
|
byte[] hashBytes = Hash(msgBytes);
|
||||||
return BitConverter.ToString(hashBytes).Replace("-", string.Empty);
|
return EncodedString.ToHexString(hashBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,12 +46,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
|
|||||||
{
|
{
|
||||||
case Constants.SignTypes.MD5:
|
case Constants.SignTypes.MD5:
|
||||||
{
|
{
|
||||||
return MD5Utility.Hash(queryString).ToUpper();
|
return MD5Utility.Hash(queryString).Value!.ToUpper();
|
||||||
}
|
}
|
||||||
|
|
||||||
case Constants.SignTypes.HMAC_SHA256:
|
case Constants.SignTypes.HMAC_SHA256:
|
||||||
{
|
{
|
||||||
return HMACUtility.HashWithSHA256(secretValue, queryString).ToUpper();
|
return HMACUtility.HashWithSHA256(secretValue, queryString).Value!.ToUpper();
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
+3
-1
@@ -4,6 +4,8 @@ using Xunit;
|
|||||||
|
|
||||||
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests
|
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests
|
||||||
{
|
{
|
||||||
|
using SKIT.FlurlHttpClient.Primitives;
|
||||||
|
|
||||||
public class TestCase_ToolsAESUtilityTests
|
public class TestCase_ToolsAESUtilityTests
|
||||||
{
|
{
|
||||||
[Fact(DisplayName = "测试用例:AES-ECB 解密")]
|
[Fact(DisplayName = "测试用例:AES-ECB 解密")]
|
||||||
@@ -12,7 +14,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests
|
|||||||
string key = "01234567890abcdefedcba9876543210";
|
string key = "01234567890abcdefedcba9876543210";
|
||||||
string cipherText = "l/aL5GyVfl/tw3Lww6GvCQ==";
|
string cipherText = "l/aL5GyVfl/tw3Lww6GvCQ==";
|
||||||
|
|
||||||
string actualPlain = Utilities.AESUtility.DecryptWithECB(encodingKey: Convert.ToBase64String(Encoding.UTF8.GetBytes(key)), encodingCipherText: cipherText);
|
string actualPlain = Utilities.AESUtility.DecryptWithECB(encodingKey: (EncodedString)Convert.ToBase64String(Encoding.UTF8.GetBytes(key)), encodingCipher: (EncodedString)cipherText)!;
|
||||||
string expectedPlain = "SKIT is great!";
|
string expectedPlain = "SKIT is great!";
|
||||||
|
|
||||||
Assert.Equal(expectedPlain, actualPlain);
|
Assert.Equal(expectedPlain, actualPlain);
|
||||||
|
|||||||
Reference in New Issue
Block a user