WXBizMsgCrypt.cs 8.4 KB

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