diff --git a/src/SKIT.FlurlHttpClient.Wechat.Work/Extensions/WechatWorkClientEventExtensions.cs b/src/SKIT.FlurlHttpClient.Wechat.Work/Extensions/WechatWorkClientEventExtensions.cs
index e2c800bd..db268076 100644
--- a/src/SKIT.FlurlHttpClient.Wechat.Work/Extensions/WechatWorkClientEventExtensions.cs
+++ b/src/SKIT.FlurlHttpClient.Wechat.Work/Extensions/WechatWorkClientEventExtensions.cs
@@ -2,6 +2,7 @@
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
+using System.Xml.Linq;
using System.Xml.Serialization;
namespace SKIT.FlurlHttpClient.Wechat.Work
@@ -249,5 +250,118 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
return xml;
}
+
+ ///
+ /// 验证回调通知事件签名。
+ /// REF: https://open.work.weixin.qq.com/api/doc/90000/90139/90968
+ /// REF: https://open.work.weixin.qq.com/api/doc/90001/90148/91144
+ /// REF: https://open.work.weixin.qq.com/api/doc/90002/90156/91169
+ ///
+ ///
+ /// 微信回调通知中的 timestamp 字段。
+ /// 微信回调通知中的 nonce 字段。
+ /// 微信回调通知中的 echostr 字段。
+ /// 微信回调通知中的 msg_signature 字段。
+ ///
+ ///
+ public static bool VerifyEventSignatureForEcho(this WechatWorkClient client, string callbackTimestamp, string callbackNonce, string callbackEcho, string callbackSignature, out string? replyEcho)
+ {
+ 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 (callbackEcho == null) throw new ArgumentNullException(nameof(callbackEcho));
+ if (callbackSignature == null) throw new ArgumentNullException(nameof(callbackSignature));
+
+ try
+ {
+ bool ret = Utilities.WxBizMsgCryptor.VerifySignature(
+ sToken: client.Credentials.PushToken!,
+ sTimestamp: callbackTimestamp,
+ sNonce: callbackNonce,
+ sMsgEncrypt: callbackEcho,
+ sMsgSign: callbackSignature
+ );
+
+ if (ret)
+ {
+ replyEcho = Utilities.WxBizMsgCryptor.AESDecrypt(cipherText: callbackEcho, encodingAESKey: client.Credentials.PushEncodingAESKey!, out _);
+ return true;
+ }
+ }
+ catch { }
+
+ replyEcho = null;
+ return false;
+ }
+
+ ///
+ /// 验证回调通知事件签名。
+ /// REF: https://open.work.weixin.qq.com/api/doc/90000/90139/90968
+ /// REF: https://open.work.weixin.qq.com/api/doc/90001/90148/91144
+ /// REF: https://open.work.weixin.qq.com/api/doc/90002/90156/91169
+ ///
+ ///
+ /// 微信回调通知中的 timestamp 字段。
+ /// 微信回调通知中的 nonce 字段。
+ /// 微信回调通知中请求正文(JSON 格式)。
+ /// 微信回调通知中的 msg_signature 字段。
+ ///
+ public static bool VerifyEventSignatureFromJson(this WechatWorkClient client, string callbackTimestamp, string callbackNonce, string callbackJson, string callbackSignature)
+ {
+ 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: callbackTimestamp,
+ sNonce: callbackNonce,
+ sMsgEncrypt: encryptedEvent.EncryptedData,
+ sMsgSign: callbackSignature
+ );
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 验证回调通知事件签名。
+ /// REF: https://open.work.weixin.qq.com/api/doc/90000/90139/90968
+ /// REF: https://open.work.weixin.qq.com/api/doc/90001/90148/91144
+ /// REF: https://open.work.weixin.qq.com/api/doc/90002/90156/91169
+ ///
+ ///
+ /// 微信回调通知中的 timestamp 字段。
+ /// 微信回调通知中的 nonce 字段。
+ /// 微信回调通知中请求正文(XML 格式)。
+ /// 微信回调通知中的 msg_signature 字段。
+ ///
+ public static bool VerifyEventSignatureFromXml(this WechatWorkClient client, string callbackTimestamp, string callbackNonce, string callbackXml, string callbackSignature)
+ {
+ if (client == null) throw new ArgumentNullException(nameof(client));
+ if (callbackXml == null) throw new ArgumentNullException(nameof(callbackXml));
+
+ try
+ {
+ XDocument xDoc = XDocument.Parse(callbackXml);
+ string? msgEncrypt = xDoc.Root?.Element("Encrypt")?.Value;
+
+ return Utilities.WxBizMsgCryptor.VerifySignature(
+ sToken: client.Credentials.PushToken!,
+ sTimestamp: callbackTimestamp,
+ sNonce: callbackNonce,
+ sMsgEncrypt: msgEncrypt!,
+ sMsgSign: callbackSignature
+ );
+ }
+ catch
+ {
+ return false;
+ }
+ }
}
}