diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventExtensions.cs index c106d660..e2f9bbac 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventExtensions.cs @@ -183,7 +183,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api List tmp = new List(capacity: 3) { client.Credentials.PushToken!, webhookTimestamp, webhookNonce }; tmp.Sort(StringComparer.Ordinal); - string sign = Utilities.SHA1Utility.Hash(string.Concat(tmp)); + string sign = Utilities.SHA1Utility.Hash(string.Concat(tmp)).Value!; return string.Equals(sign, webhookSignature, StringComparison.OrdinalIgnoreCase); } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinExpressExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinExpressExtensions.cs index c83b08b9..a6a0fb36 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinExpressExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinExpressExtensions.cs @@ -23,7 +23,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api if (request.DeliverySignature is null) { string msgText = $"{request.ShopId}{request.ShopOrderId}{client.Credentials.ImmeDeliveryAppSecret}"; - request.DeliverySignature = Utilities.SHA1Utility.Hash(msgText).ToLower(); + request.DeliverySignature = Utilities.SHA1Utility.Hash(msgText).Value!.ToLower(); } return request; diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMidasExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMidasExtensions.cs index fa9a8e54..51864e7e 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMidasExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteCgibinMidasExtensions.cs @@ -50,7 +50,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api + $"&org_loc={request.GetRequestPath()}" + $"&method={request.GetRequestMethod()}" + $"&secret={client.Credentials.MidasAppKey}"; - request.Signature = Utilities.HMACUtility.HashWithSHA256(client.Credentials.MidasAppKey ?? string.Empty, msgText).ToLower(); + request.Signature = Utilities.HMACUtility.HashWithSHA256(client.Credentials.MidasAppKey ?? string.Empty, msgText).Value!.ToLower(); } return request; diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaGameExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaGameExtensions.cs index 0223e78d..41b422ae 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaGameExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteWxaGameExtensions.cs @@ -41,7 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api case Constants.MidasSignMethods.HMAC_SHA256: { string msgText = tmpRawData; - request.Signature = Utilities.HMACUtility.HashWithSHA256(request.SessionKey, msgText).ToLower(); + request.Signature = Utilities.HMACUtility.HashWithSHA256(request.SessionKey, msgText).Value!.ToLower(); } break; @@ -58,7 +58,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api tmpRawData = tmpRawData ?? client.JsonSerializer.Serialize(request); string msgText = $"{request.GetRequestPath()}&{tmpRawData}"; - request.PaySign = Utilities.HMACUtility.HashWithSHA256(client.Credentials.MidasAppKeyV2 ?? string.Empty, msgText).ToLower(); + request.PaySign = Utilities.HMACUtility.HashWithSHA256(client.Credentials.MidasAppKeyV2 ?? string.Empty, msgText).Value!.ToLower(); } return request; diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteXPayExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteXPayExtensions.cs index 2a42208c..d88eb958 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteXPayExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientExecuteXPayExtensions.cs @@ -22,7 +22,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api tmpRawData = tmpRawData ?? client.JsonSerializer.Serialize(request); string msgText = tmpRawData; - request.Signature = Utilities.HMACUtility.HashWithSHA256(request.SessionKey, msgText).ToLower(); + request.Signature = Utilities.HMACUtility.HashWithSHA256(request.SessionKey, msgText).Value!.ToLower(); } if (request.PaySign is null) @@ -30,7 +30,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api tmpRawData = tmpRawData ?? client.JsonSerializer.Serialize(request); string msgText = $"{request.GetRequestPath()}&{tmpRawData}"; - request.PaySign = Utilities.HMACUtility.HashWithSHA256(client.Credentials.VirtualPaymentAppKey ?? string.Empty, msgText).ToLower(); + request.PaySign = Utilities.HMACUtility.HashWithSHA256(client.Credentials.VirtualPaymentAppKey ?? string.Empty, msgText).Value!.ToLower(); } return request; diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientParameterExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientParameterExtensions.cs index 6a340235..ad63c77e 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientParameterExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientParameterExtensions.cs @@ -27,7 +27,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api string timestamp = DateTimeOffset.Now.ToLocalTime().ToUnixTimeSeconds().ToString(); string nonce = Guid.NewGuid().ToString("N"); - string sign = Utilities.SHA1Utility.Hash($"jsapi_ticket={jsapiTicket}&noncestr={nonce}×tamp={timestamp}&url={url.Split('#')[0]}").ToLower(); + string sign = Utilities.SHA1Utility.Hash($"jsapi_ticket={jsapiTicket}&noncestr={nonce}×tamp={timestamp}&url={url.Split('#')[0]}").Value!.ToLower(); return new ReadOnlyDictionary(new Dictionary() { @@ -56,7 +56,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api List tmp = new List(capacity: 5) { cardType, timestamp, client.Credentials.AppId, nonce, wxcardTicket }; tmp.Sort(StringComparer.Ordinal); - string cardSign = Utilities.SHA1Utility.Hash(string.Concat(tmp)).ToLower(); + string cardSign = Utilities.SHA1Utility.Hash(string.Concat(tmp)).Value!.ToLower(); return new ReadOnlyDictionary(new Dictionary() { diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/AESUtility.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/AESUtility.cs index 083cf881..7e08bb5c 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/AESUtility.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/AESUtility.cs @@ -1,9 +1,10 @@ using System; using System.Security.Cryptography; -using System.Text; namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities { + using SKIT.FlurlHttpClient.Primitives; + /// /// AES 算法工具类。 /// @@ -37,21 +38,22 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities /// /// 基于 CBC 模式解密数据。 /// - /// 经 Base64 编码后的 AES 密钥。 - /// 经 Base64 编码后的 AES 初始化向量。 - /// 经 Base64 编码后的待解密数据。 - /// 解密后的文本数据。 - public static string DecryptWithCBC(string encodingKey, string encodingIV, string encodingCipherText) + /// 经过编码后的(通常为 Base64)AES 密钥。 + /// 经过编码后的(通常为 Base64)初始化向量。 + /// 经过编码后的(通常为 Base64)待解密数据。 + /// 解密后的数据。 + public static EncodedString DecryptWithCBC(EncodedString encodingKey, EncodedString encodingIV, 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 (encodingIV.Value is null) throw new ArgumentNullException(nameof(encodingIV)); + if (encodingCipher.Value is null) throw new ArgumentNullException(nameof(encodingCipher)); byte[] plainBytes = DecryptWithCBC( - keyBytes: Convert.FromBase64String(encodingKey), - ivBytes: Convert.FromBase64String(encodingIV), - cipherBytes: Convert.FromBase64String(encodingCipherText) + keyBytes: EncodedString.FromString(encodingKey, fallbackEncodingKind: EncodingKinds.Base64), + ivBytes: EncodedString.FromString(encodingIV, fallbackEncodingKind: EncodingKinds.Base64), + cipherBytes: EncodedString.FromString(encodingCipher, fallbackEncodingKind: EncodingKinds.Base64) ); - return Encoding.UTF8.GetString(plainBytes); + return EncodedString.ToLiteralString(plainBytes); } /// @@ -82,21 +84,22 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities /// /// 基于 CBC 模式加密数据。 /// - /// 经 Base64 编码后的 AES 密钥。 - /// 经 Base64 编码后的 AES 初始化向量。 - /// 待加密文本。 - /// 经 Base64 编码的加密后的数据。 - public static string EncryptWithCBC(string encodingKey, string encodingIV, string plainText) + /// 经过编码后的(通常为 Base64)AES 密钥。 + /// 经过编码后的(通常为 Base64)初始化向量。 + /// 待加密数据。 + /// 经过 Base64 编码的加密后的数据。 + public static EncodedString EncryptWithCBC(EncodedString encodingKey, EncodedString encodingIV, string plainData) { - if (encodingKey is null) throw new ArgumentNullException(nameof(encodingKey)); - if (plainText is null) throw new ArgumentNullException(nameof(plainText)); + if (encodingKey.Value is null) throw new ArgumentNullException(nameof(encodingKey)); + if (encodingIV.Value is null) throw new ArgumentNullException(nameof(encodingIV)); + if (plainData is null) throw new ArgumentNullException(nameof(plainData)); byte[] plainBytes = EncryptWithCBC( - keyBytes: Convert.FromBase64String(encodingKey), - ivBytes: Convert.FromBase64String(encodingIV), - plainBytes: Encoding.UTF8.GetBytes(plainText) + keyBytes: EncodedString.FromString(encodingKey, fallbackEncodingKind: EncodingKinds.Base64), + ivBytes: EncodedString.FromString(encodingIV, fallbackEncodingKind: EncodingKinds.Base64), + plainBytes: EncodedString.FromLiteralString(plainData) ); - return Convert.ToBase64String(plainBytes); + return EncodedString.ToBase64String(plainBytes); } } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/HMACUtility.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/HMACUtility.cs index a84a3b4a..61455d1b 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/HMACUtility.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/HMACUtility.cs @@ -1,44 +1,49 @@ using System; using System.Security.Cryptography; -using System.Text; namespace SKIT.FlurlHttpClient.Wechat.Api.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.Api/Utilities/SHA1Utility.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/SHA1Utility.cs index 6036259f..4306ae24 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/SHA1Utility.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/SHA1Utility.cs @@ -1,39 +1,44 @@ using System; using System.Security.Cryptography; -using System.Text; namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities { + using SKIT.FlurlHttpClient.Primitives; + /// /// SHA-1 算法工具类。 /// public static class SHA1Utility { /// - /// 获取 SHA-1 信息摘要。 + /// 计算 SHA-1 哈希值。 /// - /// 信息字节数组。 - /// 信息摘要字节数组。 + /// 要计算哈希值的信息字节数组。 + /// 哈希值字节数组。 public static byte[] Hash(byte[] bytes) { if (bytes is null) throw new ArgumentNullException(nameof(bytes)); - using SHA1 sha = SHA1.Create(); - return sha.ComputeHash(bytes); +#if NET5_0_OR_GREATER + return SHA1.HashData(bytes); +#else + using SHA1 sha1 = SHA1.Create(); + return sha1.ComputeHash(bytes); +#endif } /// - /// 获取 SHA-1 信息摘要。 + /// 计算 SHA-1 哈希值。 /// - /// 文本信息。 - /// 信息摘要。 - 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.Api/Utilities/__Internal/WxMsgCryptor.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/__Internal/WxMsgCryptor.cs index ddde1f55..df535a21 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/__Internal/WxMsgCryptor.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Utilities/__Internal/WxMsgCryptor.cs @@ -233,7 +233,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities tmp.Sort(StringComparer.Ordinal); string rawText = string.Join(string.Empty, tmp); - string signText = SHA1Utility.Hash(rawText); + string signText = SHA1Utility.Hash(rawText).Value!; return signText.ToLower(); } diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_ToolsAESUtilityTests.cs b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_ToolsAESUtilityTests.cs index 0e154e47..8635b9c8 100644 --- a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_ToolsAESUtilityTests.cs +++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_ToolsAESUtilityTests.cs @@ -2,6 +2,8 @@ using Xunit; namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests { + using SKIT.FlurlHttpClient.Primitives; + public class TestCase_ToolsAESUtilityTests { [Fact(DisplayName = "测试用例:AES-CBC 解密")] @@ -12,7 +14,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests string cipherText = "Gu2PVnxVWl+jK4F8c0liGxfkB5Bj3m5HRvwgEIk1Yb+36RZ3Bg7YmUnud/ooiHz0PQroipsH7GCjlGwUeT04NwmrFaP1y3dRYPLpS43ed9QZWcFIFo+8vTs3Zco6S98DUvaNEAs8duhz/BzfBOZaIHMziRqEtPFI0ZDzCgJluBirJ6Wl3UkygZ5/QLo3KA53qGdip7K48Rq8XbCwuidTCw=="; string expectedPlainData = "{\"phoneNumber\":\"186****5613\",\"purePhoneNumber\":\"186****5613\",\"countryCode\":\"86\",\"watermark\":{\"timestamp\":1634545675,\"appid\":\"wxc****17e87e0e0a7\"}}"; - string actualPlainData = Utilities.AESUtility.DecryptWithCBC(encodingKey: key, encodingIV: iv, encodingCipherText: cipherText); + string actualPlainData = Utilities.AESUtility.DecryptWithCBC(encodingKey: new EncodedString(key, EncodingKinds.Base64), encodingIV: new EncodedString(iv, EncodingKinds.Base64), encodingCipher: new EncodedString(cipherText, EncodingKinds.Base64))!; Assert.Equal(expectedPlainData, actualPlainData, ignoreCase: true); } diff --git a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_ToolsHashUtilityTests.cs b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_ToolsHashUtilityTests.cs index 995b962f..05e4b94a 100644 --- a/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_ToolsHashUtilityTests.cs +++ b/test/SKIT.FlurlHttpClient.Wechat.Api.UnitTests/TestCase_ToolsHashUtilityTests.cs @@ -9,7 +9,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests { string rawData = "jsapi_ticket=HoagFKDcsGMVCIY2vOjf9nGrZ3eaM0qXs5ROFN_3k_HrGc0VocemA6wMXkvrL-Ei4IitXxwKF62CJWR8mWXZ3Q&noncestr=e7b435f73835402da44f16640ddc8696×tamp=1621348162&url=https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign"; - string actualHash = Utilities.SHA1Utility.Hash(rawData); + string actualHash = Utilities.SHA1Utility.Hash(rawData)!; string expectedHash = "b214ea1f8ae019c207f8c6ffb843c8474cbab28c"; Assert.Equal(expectedHash, actualHash, ignoreCase: true); @@ -21,7 +21,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.UnitTests string secret = "zNLgAGgqsEWJOg1nFVaO5r7fAlIQxr1u"; string rawData = "appid=wx1234567&offer_id=12345678&openid=odkx20ENSNa2w5y3g_qOkOvBNM1g&pf=android&ts=1507530737&zone_id=1&org_loc=/cgi-bin/midas/getbalance&method=POST&secret=zNLgAGgqsEWJOg1nFVaO5r7fAlIQxr1u"; - string actualHash = Utilities.HMACUtility.HashWithSHA256(secret, rawData); + string actualHash = Utilities.HMACUtility.HashWithSHA256(secret, rawData)!; string expectedHash = "1ad64e8dcb2ec1dc486b7fdf01f4a15159fc623dc3422470e51cf6870734726b"; Assert.Equal(expectedHash, actualHash, ignoreCase: true);