diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Extensions/WechatTenpayClientEventDecryptionExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Extensions/WechatTenpayClientEventDecryptionExtensions.cs index 2dea504e..58892659 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Extensions/WechatTenpayClientEventDecryptionExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Extensions/WechatTenpayClientEventDecryptionExtensions.cs @@ -1,8 +1,9 @@ using System; -using System.Text; namespace SKIT.FlurlHttpClient.Wechat.TenpayV2 { + using SKIT.FlurlHttpClient.Primitives; + /// /// 为 提供回调通知事件敏感数据解密的扩展方法。 /// @@ -56,11 +57,11 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2 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( - encodingKey: Convert.ToBase64String(Encoding.UTF8.GetBytes(key)), - encodingCipherText: webhookEvent.EncryptedRequestInfo! - ); + encodingKey: new EncodedString(key, EncodingKinds.Literal), + encodingCipher: new EncodedString(webhookEvent.EncryptedRequestInfo!, EncodingKinds.Base64) + )!; plainJson = Utilities.XmlHelper.ConvertToJson(plainXml); } catch (Exception ex) diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Extensions/WechatTenpayClientExecuteMerchantMediaExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Extensions/WechatTenpayClientExecuteMerchantMediaExtensions.cs index 6af28c45..e564a65c 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Extensions/WechatTenpayClientExecuteMerchantMediaExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Extensions/WechatTenpayClientExecuteMerchantMediaExtensions.cs @@ -10,6 +10,8 @@ using Flurl.Http; namespace SKIT.FlurlHttpClient.Wechat.TenpayV2 { + using SKIT.FlurlHttpClient.Primitives; + public static class WechatTenpayClientExecuteMerchantMediaExtensions { /// @@ -29,7 +31,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2 request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".jpg"; if (request.FileHash is null) - request.FileHash = BitConverter.ToString(Utilities.MD5Utility.Hash(request.FileBytes ?? Array.Empty())).Replace("-", string.Empty).ToLower(); + request.FileHash = EncodedString.ToHexString(Utilities.MD5Utility.Hash(request.FileBytes ?? Array.Empty())).Value!.ToLower(); IFlurlRequest flurlReq = client .CreateFlurlRequest(request, HttpMethod.Post, "secapi", "mch", "uploadmedia"); diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Models/VehiclePartner/GetTransitPartnerOrderResponse.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Models/VehiclePartner/GetTransitPartnerOrderResponse.cs index ebfecaa3..d15ff29c 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Models/VehiclePartner/GetTransitPartnerOrderResponse.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Models/VehiclePartner/GetTransitPartnerOrderResponse.cs @@ -10,7 +10,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Models [System.Text.Json.Serialization.JsonConverter(typeof(Converters.ResponseClassSystemTextJsonConverter))] public class GetTransitPartnerOrderResponse : WechatTenpaySignableResponse { - public static new class Types + public static class Types { public class Coupon : GetTransitOrderResponse.Types.Coupon { diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/AESUtility.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/AESUtility.cs index 717094f4..18b5f965 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/AESUtility.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/AESUtility.cs @@ -1,9 +1,10 @@ using System; using System.Security.Cryptography; -using System.Text; namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities { + using SKIT.FlurlHttpClient.Primitives; + /// /// AES 算法工具类。 /// @@ -20,33 +21,31 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities if (keyBytes is null) throw new ArgumentNullException(nameof(keyBytes)); if (cipherBytes is null) throw new ArgumentNullException(nameof(cipherBytes)); - using (SymmetricAlgorithm aes = Aes.Create()) - { - aes.Mode = CipherMode.ECB; - aes.Padding = PaddingMode.PKCS7; - aes.Key = keyBytes; + using SymmetricAlgorithm aes = Aes.Create(); + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.PKCS7; + aes.Key = keyBytes; - using ICryptoTransform transform = aes.CreateDecryptor(); - return transform.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length); - } + using ICryptoTransform transform = aes.CreateDecryptor(); + return transform.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length); } /// /// 基于 ECB 模式解密数据。 /// - /// 经 Base64 编码后的 AES 密钥。 - /// 经 Base64 编码后的待解密数据。 - /// 解密后的文本数据。 - public static string DecryptWithECB(string encodingKey, string encodingCipherText) + /// 经过编码后的(通常为 Base64)AES 密钥。 + /// 经过编码后的(通常为 Base64)待解密数据。 + /// 解密后的数据。 + public static EncodedString DecryptWithECB(EncodedString encodingKey, EncodedString encodingCipher) { - if (encodingKey is null) throw new ArgumentNullException(nameof(encodingKey)); - if (encodingCipherText is null) throw new ArgumentNullException(nameof(encodingCipherText)); + if (encodingKey.Value is null) throw new ArgumentNullException(nameof(encodingKey)); + if (encodingCipher.Value is null) throw new ArgumentNullException(nameof(encodingCipher)); byte[] plainBytes = DecryptWithECB( - keyBytes: Convert.FromBase64String(encodingKey), - cipherBytes: Convert.FromBase64String(encodingCipherText) + keyBytes: EncodedString.FromString(encodingKey, fallbackEncodingKind: EncodingKinds.Base64), + cipherBytes: EncodedString.FromString(encodingCipher, fallbackEncodingKind: EncodingKinds.Base64) ); - return Encoding.UTF8.GetString(plainBytes); + return EncodedString.ToLiteralString(plainBytes); } } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/HMACUtility.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/HMACUtility.cs index 88dca83f..b97e7c10 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/HMACUtility.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/HMACUtility.cs @@ -1,44 +1,49 @@ using System; using System.Security.Cryptography; -using System.Text; namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities { + using SKIT.FlurlHttpClient.Primitives; + /// /// HMAC 算法工具类。 /// public static class HMACUtility { /// - /// 获取 HMAC-SHA-256 消息认证码。 + /// 计算 HMAC-SHA-256 哈希值。 /// - /// 密钥字节数组。 - /// 信息字节数组。 - /// 消息认证码字节数组。 - 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)); - 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); +#endif } /// - /// 获取 HMAC-SHA-256 消息认证码。 + /// 计算 HMAC-SHA-256 哈希值。 /// - /// 密钥。 - /// 文本信息。 - /// 消息认证码。 - 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)); - byte[] secretBytes = Encoding.UTF8.GetBytes(secret); - byte[] msgBytes = Encoding.UTF8.GetBytes(message); - byte[] hashBytes = HashWithSHA256(secretBytes, msgBytes); - return BitConverter.ToString(hashBytes).Replace("-", string.Empty); + byte[] keyBytes = EncodedString.FromLiteralString(key); + byte[] msgBytes = EncodedString.FromLiteralString(message); + byte[] hashBytes = HashWithSHA256(keyBytes, msgBytes); + return EncodedString.ToHexString(hashBytes); } } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/MD5Utility.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/MD5Utility.cs index 526fe7ea..3580d9d6 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/MD5Utility.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/MD5Utility.cs @@ -1,39 +1,44 @@ using System; using System.Security.Cryptography; -using System.Text; namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities { + using SKIT.FlurlHttpClient.Primitives; + /// /// MD5 算法工具类。 /// public static class MD5Utility { /// - /// 获取 MD5 信息摘要。 + /// 计算 MD5 哈希值。 /// - /// 信息字节数组。 - /// 信息摘要字节数组。 + /// 要计算哈希值的信息字节数组。 + /// 哈希值字节数组。 public static byte[] Hash(byte[] 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(); return md5.ComputeHash(bytes); +#endif } /// - /// 获取 MD5 信息摘要。 + /// 计算 MD5 哈希值。 /// - /// 文本信息。 - /// 信息摘要。 - public static string Hash(string message) + /// 要计算哈希值的信息。 + /// 经过十六进制编码的哈希值。 + public static EncodedString Hash(string 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); - return BitConverter.ToString(hashBytes).Replace("-", string.Empty); + return EncodedString.ToHexString(hashBytes); } } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/__Internal/RequestSigner.cs b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/__Internal/RequestSigner.cs index 95afc69a..06003af7 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/__Internal/RequestSigner.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.TenpayV2/Utilities/__Internal/RequestSigner.cs @@ -46,12 +46,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities { case Constants.SignTypes.MD5: { - return MD5Utility.Hash(queryString).ToUpper(); + return MD5Utility.Hash(queryString).Value!.ToUpper(); } case Constants.SignTypes.HMAC_SHA256: { - return HMACUtility.HashWithSHA256(secretValue, queryString).ToUpper(); + return HMACUtility.HashWithSHA256(secretValue, queryString).Value!.ToUpper(); } default: diff --git a/test/SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests/TestCase_ToolsAESUtilityTests.cs b/test/SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests/TestCase_ToolsAESUtilityTests.cs index 636b04d9..1c6bceea 100644 --- a/test/SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests/TestCase_ToolsAESUtilityTests.cs +++ b/test/SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests/TestCase_ToolsAESUtilityTests.cs @@ -4,6 +4,8 @@ using Xunit; namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests { + using SKIT.FlurlHttpClient.Primitives; + public class TestCase_ToolsAESUtilityTests { [Fact(DisplayName = "测试用例:AES-ECB 解密")] @@ -12,7 +14,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.UnitTests string key = "01234567890abcdefedcba9876543210"; 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!"; Assert.Equal(expectedPlain, actualPlain);