diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventSerializationExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventExtensions.cs similarity index 75% rename from src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventSerializationExtensions.cs rename to src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventExtensions.cs index df8b867f..e5c64149 100644 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventSerializationExtensions.cs +++ b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventExtensions.cs @@ -4,14 +4,15 @@ using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using System.Xml.Linq; using System.Xml.Serialization; namespace SKIT.FlurlHttpClient.Wechat.Api { /// - /// 为 提供回调通知事件序列化相关的扩展方法。 + /// 为 提供回调通知事件的扩展方法。 /// - public static class WechatApiClientEventSerializationExtensions + public static class WechatApiClientEventExtensions { private class EncryptedWechatApiEvent { @@ -270,5 +271,89 @@ namespace SKIT.FlurlHttpClient.Wechat.Api return xml; } + + /// + /// 验证回调通知事件签名。 + /// REF: https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html + /// + /// + /// 微信回调通知中的 timestamp 字段。 + /// 微信回调通知中的 nonce 字段。 + /// 微信回调通知中的 signature 字段。 + /// + public static bool VerifyEventSignatureForEcho(this WechatApiClient client, string callbackTimestamp, string callbackNonce, string callbackSignature) + { + if (client == null) throw new ArgumentNullException(nameof(client)); + if (callbackTimestamp == null) throw new ArgumentNullException(nameof(callbackTimestamp)); + if (callbackNonce == null) throw new ArgumentNullException(nameof(callbackNonce)); + if (callbackSignature == null) throw new ArgumentNullException(nameof(callbackSignature)); + + ISet set = new SortedSet() { client.Credentials.PushToken!, callbackTimestamp, callbackNonce }; + string sign = Security.SHA1Utility.Hash(string.Concat(set)); + return string.Equals(sign, callbackSignature, StringComparison.InvariantCultureIgnoreCase); + } + + /// + /// 验证回调通知事件签名。 + /// REF: https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html + /// + /// + /// + /// + public static bool VerifyEventSignatureFromJson(this WechatApiClient client, string callbackJson) + { + if (client == null) throw new ArgumentNullException(nameof(client)); + if (callbackJson == null) throw new ArgumentNullException(nameof(callbackJson)); + + try + { + var encryptedEvent = client.JsonSerializer.Deserialize(callbackJson); + return Utilities.WxBizMsgCryptor.VerifySignature( + sToken: client.Credentials.PushToken!, + sTimestamp: encryptedEvent.Timestamp, + sNonce: encryptedEvent.Nonce, + sMsgEncrypt: encryptedEvent.EncryptedData, + sMsgSign: encryptedEvent.Signature + ); + } + catch + { + return false; + } + } + + /// + /// 验证回调通知事件签名。 + /// REF: https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html + /// + /// + /// + /// + public static bool VerifyEventSignatureFromXml(this WechatApiClient client, string callbackXml) + { + if (client == null) throw new ArgumentNullException(nameof(client)); + if (callbackXml == null) throw new ArgumentNullException(nameof(callbackXml)); + + try + { + XDocument xDoc = XDocument.Load(callbackXml); + string? timestamp = xDoc.Root?.Element("TimeStamp")?.Value; + string? nonce = xDoc.Root?.Element("Nonce")?.Value; + string? msgEncrypt = xDoc.Root?.Element("Encrypt")?.Value; + string? msgSignature = xDoc.Root?.Element("MsgSignature")?.Value; + + return Utilities.WxBizMsgCryptor.VerifySignature( + sToken: client.Credentials.PushToken!, + sTimestamp: timestamp!, + sNonce: nonce!, + sMsgEncrypt: msgEncrypt!, + sMsgSign: msgEncrypt! + ); + } + catch + { + return false; + } + } } } diff --git a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventVerificationExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventVerificationExtensions.cs deleted file mode 100644 index 7a8c50ae..00000000 --- a/src/SKIT.FlurlHttpClient.Wechat.Api/Extensions/WechatApiClientEventVerificationExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; - -namespace SKIT.FlurlHttpClient.Wechat.Api -{ - /// - /// 为 提供回调通知事件验证相关的扩展方法。 - /// - public static class WechatApiClientEventVerificationExtensions - { - /// - /// 验证回调通知事件签名。 - /// REF: https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html - /// - /// - /// 微信回调通知中的 timestamp 字段。 - /// 微信回调通知中的 nonce 字段。 - /// 微信回调通知中的 signature 字段。 - /// - public static bool VerifyEventSignature( - this WechatApiClient client, - string callbackTimestamp, - string callbackNonce, - string callbackSignature) - { - if (client == null) throw new ArgumentNullException(nameof(client)); - if (callbackTimestamp == null) throw new ArgumentNullException(nameof(callbackTimestamp)); - if (callbackNonce == null) throw new ArgumentNullException(nameof(callbackNonce)); - if (callbackSignature == null) throw new ArgumentNullException(nameof(callbackSignature)); - - ISet set = new SortedSet() { client.Credentials.PushToken!, callbackTimestamp, callbackNonce }; - string sign = Security.SHA1Utility.Hash(string.Concat(set)); - return string.Equals(sign, callbackSignature, StringComparison.InvariantCultureIgnoreCase); - } - } -}