You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

258 lines
9.9 KiB

  1. using System;
  2. using System.Text;
  3. using System.Xml;
  4. using System.Collections;
  5. //using System.Web;
  6. using System.Security.Cryptography;
  7. //-40001 : 签名验证错误
  8. //-40002 : xml解析失败
  9. //-40003 : sha加密生成签名失败
  10. //-40004 : AESKey 非法
  11. //-40005 : corpid 校验错误
  12. //-40006 : AES 加密失败
  13. //-40007 : AES 解密失败
  14. //-40008 : 解密后得到的buffer非法
  15. //-40009 : base64加密异常
  16. //-40010 : base64解密异常
  17. namespace Learun.Application.WeChat
  18. {
  19. public class WXBizMsgCrypt
  20. {
  21. string m_sToken;
  22. string m_sEncodingAESKey;
  23. string m_sCorpID;
  24. enum WXBizMsgCryptErrorCode
  25. {
  26. WXBizMsgCrypt_OK = 0,
  27. WXBizMsgCrypt_ValidateSignature_Error = -40001,
  28. WXBizMsgCrypt_ParseXml_Error = -40002,
  29. WXBizMsgCrypt_ComputeSignature_Error = -40003,
  30. WXBizMsgCrypt_IllegalAesKey = -40004,
  31. WXBizMsgCrypt_ValidateCorpid_Error = -40005,
  32. WXBizMsgCrypt_EncryptAES_Error = -40006,
  33. WXBizMsgCrypt_DecryptAES_Error = -40007,
  34. WXBizMsgCrypt_IllegalBuffer = -40008,
  35. WXBizMsgCrypt_EncodeBase64_Error = -40009,
  36. WXBizMsgCrypt_DecodeBase64_Error = -40010
  37. };
  38. //构造函数
  39. // @param sToken: 公众平台上,开发者设置的Token
  40. // @param sEncodingAESKey: 公众平台上,开发者设置的EncodingAESKey
  41. // @param sCorpID: 企业号的CorpID
  42. public WXBizMsgCrypt(string sToken, string sEncodingAESKey, string sCorpID)
  43. {
  44. m_sToken = sToken;
  45. m_sCorpID = sCorpID;
  46. m_sEncodingAESKey = sEncodingAESKey;
  47. }
  48. //验证URL
  49. // @param sMsgSignature: 签名串,对应URL参数的msg_signature
  50. // @param sTimeStamp: 时间戳,对应URL参数的timestamp
  51. // @param sNonce: 随机串,对应URL参数的nonce
  52. // @param sEchoStr: 随机串,对应URL参数的echostr
  53. // @param sReplyEchoStr: 解密之后的echostr,当return返回0时有效
  54. // @return:成功0,失败返回对应的错误码
  55. public int VerifyURL(string sMsgSignature, string sTimeStamp, string sNonce, string sEchoStr, ref string sReplyEchoStr)
  56. {
  57. int ret = 0;
  58. if (m_sEncodingAESKey.Length != 43)
  59. {
  60. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
  61. }
  62. ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEchoStr, sMsgSignature);
  63. if (0 != ret)
  64. {
  65. return ret;
  66. }
  67. sReplyEchoStr = "";
  68. string cpid = "";
  69. try
  70. {
  71. sReplyEchoStr = Cryptography.AES_decrypt(sEchoStr, m_sEncodingAESKey, ref cpid); //m_sCorpID);
  72. }
  73. catch (Exception)
  74. {
  75. sReplyEchoStr = "";
  76. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error;
  77. }
  78. if (cpid != m_sCorpID)
  79. {
  80. sReplyEchoStr = "";
  81. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateCorpid_Error;
  82. }
  83. return 0;
  84. }
  85. // 检验消息的真实性,并且获取解密后的明文
  86. // @param sMsgSignature: 签名串,对应URL参数的msg_signature
  87. // @param sTimeStamp: 时间戳,对应URL参数的timestamp
  88. // @param sNonce: 随机串,对应URL参数的nonce
  89. // @param sPostData: 密文,对应POST请求的数据
  90. // @param sMsg: 解密后的原文,当return返回0时有效
  91. // @return: 成功0,失败返回对应的错误码
  92. public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg)
  93. {
  94. if (m_sEncodingAESKey.Length != 43)
  95. {
  96. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
  97. }
  98. XmlDocument doc = new XmlDocument();
  99. XmlNode root;
  100. string sEncryptMsg;
  101. try
  102. {
  103. doc.LoadXml(sPostData);
  104. root = doc.FirstChild;
  105. sEncryptMsg = root["Encrypt"].InnerText;
  106. }
  107. catch (Exception)
  108. {
  109. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error;
  110. }
  111. //verify signature
  112. int ret = 0;
  113. ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature);
  114. if (ret != 0)
  115. return ret;
  116. //decrypt
  117. string cpid = "";
  118. try
  119. {
  120. sMsg = Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid);
  121. }
  122. catch (FormatException)
  123. {
  124. sMsg = "";
  125. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error;
  126. }
  127. catch (Exception)
  128. {
  129. sMsg = "";
  130. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error;
  131. }
  132. if (cpid != m_sCorpID)
  133. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateCorpid_Error;
  134. return 0;
  135. }
  136. //将企业号回复用户的消息加密打包
  137. // @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串
  138. // @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp
  139. // @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce
  140. // @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
  141. // 当return返回0时有效
  142. // return:成功0,失败返回对应的错误码
  143. public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg)
  144. {
  145. if (m_sEncodingAESKey.Length != 43)
  146. {
  147. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;
  148. }
  149. string raw = "";
  150. try
  151. {
  152. raw = Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sCorpID);
  153. }
  154. catch (Exception)
  155. {
  156. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error;
  157. }
  158. string MsgSigature = "";
  159. int ret = 0;
  160. ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature);
  161. if (0 != ret)
  162. return ret;
  163. sEncryptMsg = "";
  164. string EncryptLabelHead = "<Encrypt><![CDATA[";
  165. string EncryptLabelTail = "]]></Encrypt>";
  166. string MsgSigLabelHead = "<MsgSignature><![CDATA[";
  167. string MsgSigLabelTail = "]]></MsgSignature>";
  168. string TimeStampLabelHead = "<TimeStamp><![CDATA[";
  169. string TimeStampLabelTail = "]]></TimeStamp>";
  170. string NonceLabelHead = "<Nonce><![CDATA[";
  171. string NonceLabelTail = "]]></Nonce>";
  172. sEncryptMsg = sEncryptMsg + "<xml>" + EncryptLabelHead + raw + EncryptLabelTail;
  173. sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail;
  174. sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail;
  175. sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail;
  176. sEncryptMsg += "</xml>";
  177. return 0;
  178. }
  179. public class DictionarySort : System.Collections.IComparer
  180. {
  181. public int Compare(object oLeft, object oRight)
  182. {
  183. string sLeft = oLeft as string;
  184. string sRight = oRight as string;
  185. int iLeftLength = sLeft.Length;
  186. int iRightLength = sRight.Length;
  187. int index = 0;
  188. while (index < iLeftLength && index < iRightLength)
  189. {
  190. if (sLeft[index] < sRight[index])
  191. return -1;
  192. else if (sLeft[index] > sRight[index])
  193. return 1;
  194. else
  195. index++;
  196. }
  197. return iLeftLength - iRightLength;
  198. }
  199. }
  200. //Verify Signature
  201. private static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture)
  202. {
  203. string hash = "";
  204. int ret = 0;
  205. ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash);
  206. if (ret != 0)
  207. return ret;
  208. if (hash == sSigture)
  209. return 0;
  210. else
  211. {
  212. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error;
  213. }
  214. }
  215. public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, ref string sMsgSignature)
  216. {
  217. ArrayList AL = new ArrayList();
  218. AL.Add(sToken);
  219. AL.Add(sTimeStamp);
  220. AL.Add(sNonce);
  221. AL.Add(sMsgEncrypt);
  222. AL.Sort(new DictionarySort());
  223. string raw = "";
  224. for (int i = 0; i < AL.Count; ++i)
  225. {
  226. raw += AL[i];
  227. }
  228. SHA1 sha;
  229. ASCIIEncoding enc;
  230. string hash = "";
  231. try
  232. {
  233. sha = new SHA1CryptoServiceProvider();
  234. enc = new ASCIIEncoding();
  235. byte[] dataToHash = enc.GetBytes(raw);
  236. byte[] dataHashed = sha.ComputeHash(dataToHash);
  237. hash = BitConverter.ToString(dataHashed).Replace("-", "");
  238. hash = hash.ToLower();
  239. }
  240. catch (Exception)
  241. {
  242. return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error;
  243. }
  244. sMsgSignature = hash;
  245. return 0;
  246. }
  247. }
  248. }