WXBizMsgCrypt.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #region Apache License Version 2.0
  2. /*----------------------------------------------------------------
  3. Copyright 2019 Jeffrey Su & Suzhou Senparc Network Technology Co.,Ltd.
  4. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  5. except in compliance with the License. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software distributed under the
  8. License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  9. either express or implied. See the License for the specific language governing permissions
  10. and limitations under the License.
  11. Detail: https://github.com/JeffreySu/WeiXinMPSDK/blob/master/license.md
  12. ----------------------------------------------------------------*/
  13. #endregion Apache License Version 2.0
  14. /*----------------------------------------------------------------
  15. 文件名:WXBizMsgCrypt.cs
  16. 文件功能描述:加解密算法
  17. 创建标识:Senparc - 20150211
  18. 修改标识:Senparc - 20150303
  19. 修改描述:整理接口
  20. ----------------------------------------------------------------*/
  21. using System;
  22. using System.Collections;
  23. using System.Security.Cryptography;
  24. using System.Text;
  25. using System.Xml;
  26. //using System.Web;
  27. //-40001 : 签名验证错误
  28. //-40002 : xml解析失败
  29. //-40003 : sha加密生成签名失败
  30. //-40004 : AESKey 非法
  31. //-40005 : appid 校验错误
  32. //-40006 : AES 加密失败
  33. //-40007 : AES 解密失败
  34. //-40008 : 解密后得到的buffer非法
  35. //-40009 : base64加密异常
  36. //-40010 : base64解密异常
  37. namespace Senparc.Weixin.Tencent
  38. {
  39. public class WXBizMsgCrypt
  40. {
  41. string m_sToken;
  42. string m_sEncodingAESKey;
  43. string m_sAppID;
  44. enum WXBizMsgCryptErrorCode
  45. {
  46. WXBizMsgCrypt_OK = 0,
  47. WXBizMsgCrypt_ValidateSignature_Error = -40001,
  48. WXBizMsgCrypt_ParseXml_Error = -40002,
  49. WXBizMsgCrypt_ComputeSignature_Error = -40003,
  50. WXBizMsgCrypt_IllegalAesKey = -40004,
  51. WXBizMsgCrypt_ValidateAppid_Error = -40005,
  52. WXBizMsgCrypt_EncryptAES_Error = -40006,
  53. WXBizMsgCrypt_DecryptAES_Error = -40007,
  54. WXBizMsgCrypt_IllegalBuffer = -40008,
  55. WXBizMsgCrypt_EncodeBase64_Error = -40009,
  56. WXBizMsgCrypt_DecodeBase64_Error = -40010
  57. };
  58. //构造函数
  59. // @param sToken: 公众平台上,开发者设置的Token
  60. // @param sEncodingAESKey: 公众平台上,开发者设置的EncodingAESKey
  61. // @param sAppID: 公众帐号的appid
  62. public WXBizMsgCrypt(string sToken, string sEncodingAESKey, string sAppID)
  63. {
  64. m_sToken = sToken;
  65. m_sAppID = sAppID;
  66. m_sEncodingAESKey = sEncodingAESKey;
  67. }
  68. // 检验消息的真实性,并且获取解密后的明文
  69. // @param sMsgSignature: 签名串,对应URL参数的msg_signature
  70. // @param sTimeStamp: 时间戳,对应URL参数的timestamp
  71. // @param sNonce: 随机串,对应URL参数的nonce
  72. // @param sPostData: 密文,对应POST请求的数据
  73. // @param sMsg: 解密后的原文,当return返回0时有效
  74. // @return: 成功0,失败返回对应的错误码
  75. public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg)
  76. {
  77. if (m_sEncodingAESKey.Length != 43)
  78. {
  79. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
  80. }
  81. XmlDocument doc = new Senparc.CO2NET.ExtensionEntities.XmlDocument_XxeFixed();
  82. XmlNode root;
  83. string sEncryptMsg;
  84. try
  85. {
  86. doc.LoadXml(sPostData);
  87. root = doc.FirstChild;
  88. sEncryptMsg = root["Encrypt"].InnerText;
  89. }
  90. catch (Exception)
  91. {
  92. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error;
  93. }
  94. //verify signature
  95. int ret = 0;
  96. ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature);
  97. if (ret != 0)
  98. return ret;
  99. //decrypt
  100. string cpid = "";
  101. try
  102. {
  103. sMsg = Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid);
  104. }
  105. catch (FormatException)
  106. {
  107. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error;
  108. }
  109. catch (Exception)
  110. {
  111. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error;
  112. }
  113. if (cpid != m_sAppID)
  114. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateAppid_Error;
  115. return 0;
  116. }
  117. //将企业号回复用户的消息加密打包
  118. // @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串
  119. // @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp
  120. // @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce
  121. // @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
  122. // 当return返回0时有效
  123. // return:成功0,失败返回对应的错误码
  124. public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg)
  125. {
  126. if (m_sEncodingAESKey.Length != 43)
  127. {
  128. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
  129. }
  130. string raw = "";
  131. try
  132. {
  133. raw = Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sAppID);
  134. }
  135. catch (Exception)
  136. {
  137. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error;
  138. }
  139. string MsgSigature = "";
  140. int ret = 0;
  141. ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature);
  142. if (0 != ret)
  143. return ret;
  144. sEncryptMsg = "";
  145. string EncryptLabelHead = "<Encrypt><![CDATA[";
  146. string EncryptLabelTail = "]]></Encrypt>";
  147. string MsgSigLabelHead = "<MsgSignature><![CDATA[";
  148. string MsgSigLabelTail = "]]></MsgSignature>";
  149. string TimeStampLabelHead = "<TimeStamp><![CDATA[";
  150. string TimeStampLabelTail = "]]></TimeStamp>";
  151. string NonceLabelHead = "<Nonce><![CDATA[";
  152. string NonceLabelTail = "]]></Nonce>";
  153. sEncryptMsg = sEncryptMsg + "<xml>" + EncryptLabelHead + raw + EncryptLabelTail;
  154. sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail;
  155. sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail;
  156. sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail;
  157. sEncryptMsg += "</xml>";
  158. return 0;
  159. }
  160. public class DictionarySort : IComparer
  161. {
  162. public int Compare(object oLeft, object oRight)
  163. {
  164. string sLeft = oLeft as string;
  165. string sRight = oRight as string;
  166. int iLeftLength = sLeft.Length;
  167. int iRightLength = sRight.Length;
  168. int index = 0;
  169. while (index < iLeftLength && index < iRightLength)
  170. {
  171. if (sLeft[index] < sRight[index])
  172. return -1;
  173. else if (sLeft[index] > sRight[index])
  174. return 1;
  175. else
  176. index++;
  177. }
  178. return iLeftLength - iRightLength;
  179. }
  180. }
  181. //Verify Signature
  182. private static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture)
  183. {
  184. string hash = "";
  185. int ret = 0;
  186. ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash);
  187. if (ret != 0)
  188. return ret;
  189. //System.Console.WriteLine(hash);
  190. if (hash == sSigture)
  191. return 0;
  192. else
  193. {
  194. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error;//-40001
  195. }
  196. }
  197. public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, ref string sMsgSignature)
  198. {
  199. ArrayList AL = new ArrayList();
  200. AL.Add(sToken);
  201. AL.Add(sTimeStamp);
  202. AL.Add(sNonce);
  203. AL.Add(sMsgEncrypt);
  204. AL.Sort(new DictionarySort());
  205. string raw = "";
  206. for (int i = 0; i < AL.Count; ++i)
  207. {
  208. raw += AL[i];
  209. }
  210. SHA1 sha;
  211. ASCIIEncoding enc;
  212. string hash = "";
  213. try
  214. {
  215. #if NET35 || NET40 || NET45
  216. sha = new SHA1CryptoServiceProvider();
  217. #else
  218. sha = SHA1.Create();
  219. #endif
  220. enc = new ASCIIEncoding();
  221. byte[] dataToHash = enc.GetBytes(raw);
  222. byte[] dataHashed = sha.ComputeHash(dataToHash);
  223. hash = BitConverter.ToString(dataHashed).Replace("-", "");
  224. hash = hash.ToLower();
  225. }
  226. catch (Exception)
  227. {
  228. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error;//-40003
  229. }
  230. sMsgSignature = hash;
  231. return 0;
  232. }
  233. }
  234. }