1、進入微信公眾號後臺設置微信伺服器配置參數(註意:Token和EncodingAESKey必須和微信伺服器驗證參數保持一致,不然驗證不會通過)。 2、設置為安全模式 3、代碼實現(主要分為驗證介面和消息處理介面): 加解密實現(微信公眾號官網有源碼) ...
1、進入微信公眾號後臺設置微信伺服器配置參數(註意:Token和EncodingAESKey必須和微信伺服器驗證參數保持一致,不然驗證不會通過)。
2、設置為安全模式
3、代碼實現(主要分為驗證介面和消息處理介面):
1 /// <summary> 2 /// 驗證介面 3 /// </summary> 4 /// <param name="signature">簽名</param> 5 /// <param name="timestamp">時間戳</param> 6 /// <param name="nonce"></param> 7 /// <param name="echostr"></param> 8 /// <returns></returns> 9 [HttpGet, Route("Message")] 10 [AllowAnonymous] 11 public ActionResult MessageGet(string signature, string timestamp, string nonce, string echostr) 12 { 13 if (new SecurityHelper().CheckSignature(signature, timestamp, nonce, _settings.Value.Token)) 14 { 15 return Content(echostr); 16 } 17 return Content(""); 18 } 19 20 /// <summary> 21 /// 接收消息並處理和返回相應結果 22 /// </summary> 23 /// <param name="msg_signature">當加密模式時才會有該變數(消息簽名)</param> 24 /// <param name="signature">簽名</param> 25 /// <param name="timestamp">時間戳</param> 26 /// <param name="nonce"></param> 27 /// <returns></returns> 28 [HttpPost, Route("Message")] 29 [AllowAnonymous] 30 public ActionResult MessagePost(string msg_signature, string signature, string timestamp, string nonce) 31 { 32 try 33 { 34 if (!new SecurityHelper().CheckSignature(signature, timestamp, nonce, _settings.Value.Token)) 35 { 36 return Content(null); 37 } 38 using (Stream stream = HttpContext.Request.Body) 39 { 40 byte[] buffer = new byte[HttpContext.Request.ContentLength.Value]; 41 stream.Read(buffer, 0, buffer.Length); 42 string content = Encoding.UTF8.GetString(buffer); 43 if (!string.IsNullOrWhiteSpace(msg_signature)) // 消息加密模式 44 { 45 string decryptMsg = string.Empty; 46 var wxBizMsgCrypt = new WXBizMsgCrypt(_settings.Value.Token, _settings.Value.EncodingAESKey, _settings.Value.AppId); 47 int decryptResult = wxBizMsgCrypt.DecryptMsg(msg_signature, timestamp, nonce, content, ref decryptMsg); 48 if (decryptResult == 0 && !string.IsNullOrWhiteSpace(decryptMsg)) 49 { 50 string resultMsg = new WechatMessageHelper().MessageResult(decryptMsg); 51 string sEncryptMsg = string.Empty; 52 if (!string.IsNullOrWhiteSpace(resultMsg)) 53 { 54 int encryptResult = wxBizMsgCrypt.EncryptMsg(resultMsg, timestamp, nonce, ref sEncryptMsg); 55 if (encryptResult == 0 && !string.IsNullOrWhiteSpace(sEncryptMsg)) 56 { 57 return Content(sEncryptMsg); 58 } 59 } 60 } 61 } 62 else // 消息未加密碼處理 63 { 64 string resultMsg = new WechatMessageHelper().MessageResult(content); 65 return Content(resultMsg); 66 } 67 return Content(null); 68 } 69 } 70 catch (Exception ex) 71 { 72 _logger.LogError("接收消息並處理和返回相應結果異常:", ex); 73 return Content(null); 74 } 75 }
加解密實現(微信公眾號官網有源碼)
1 using System; 2 using System.Collections; 3 using System.Security.Cryptography; 4 using System.Text; 5 using System.Xml; 6 7 //-40001 : 簽名驗證錯誤 8 //-40002 : xml解析失敗 9 //-40003 : sha加密生成簽名失敗 10 //-40004 : AESKey 非法 11 //-40005 : appid 校驗錯誤 12 //-40006 : AES 加密失敗 13 //-40007 : AES 解密失敗 14 //-40008 : 解密後得到的buffer非法 15 //-40009 : base64加密異常 16 //-40010 : base64解密異常 17 namespace Core.Common.Wechat 18 { 19 public class WXBizMsgCrypt 20 { 21 string m_sToken; 22 string m_sEncodingAESKey; 23 string m_sAppID; 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_ValidateAppid_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 //構造函數 40 // @param sToken: 公眾平臺上,開發者設置的Token 41 // @param sEncodingAESKey: 公眾平臺上,開發者設置的EncodingAESKey 42 // @param sAppID: 公眾帳號的appid 43 public WXBizMsgCrypt(string sToken, string sEncodingAESKey, string sAppID) 44 { 45 m_sToken = sToken; 46 m_sAppID = sAppID; 47 m_sEncodingAESKey = sEncodingAESKey; 48 } 49 50 51 // 檢驗消息的真實性,並且獲取解密後的明文 52 // @param sMsgSignature: 簽名串,對應URL參數的msg_signature 53 // @param sTimeStamp: 時間戳,對應URL參數的timestamp 54 // @param sNonce: 隨機串,對應URL參數的nonce 55 // @param sPostData: 密文,對應POST請求的數據 56 // @param sMsg: 解密後的原文,當return返回0時有效 57 // @return: 成功0,失敗返回對應的錯誤碼 58 public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg) 59 { 60 if (m_sEncodingAESKey.Length != 43) 61 { 62 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; 63 } 64 XmlDocument doc = new XmlDocument(); 65 XmlNode root; 66 string sEncryptMsg; 67 try 68 { 69 doc.LoadXml(sPostData); 70 root = doc.FirstChild; 71 sEncryptMsg = root["Encrypt"].InnerText; 72 } 73 catch (Exception) 74 { 75 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error; 76 } 77 //verify signature 78 int ret = 0; 79 ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature); 80 if (ret != 0) 81 return ret; 82 //decrypt 83 string cpid = ""; 84 try 85 { 86 sMsg = Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid); 87 } 88 catch (FormatException) 89 { 90 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error; 91 } 92 catch (Exception) 93 { 94 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error; 95 } 96 if (cpid != m_sAppID) 97 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateAppid_Error; 98 return 0; 99 } 100 101 //將企業號回覆用戶的消息加密打包 102 // @param sReplyMsg: 企業號待回覆用戶的消息,xml格式的字元串 103 // @param sTimeStamp: 時間戳,可以自己生成,也可以用URL參數的timestamp 104 // @param sNonce: 隨機串,可以自己生成,也可以用URL參數的nonce 105 // @param sEncryptMsg: 加密後的可以直接回覆用戶的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字元串, 106 // 當return返回0時有效 107 // return:成功0,失敗返回對應的錯誤碼 108 public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg) 109 { 110 if (m_sEncodingAESKey.Length != 43) 111 { 112 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey; 113 } 114 string raw = ""; 115 try 116 { 117 raw = Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sAppID); 118 } 119 catch (Exception) 120 { 121 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error; 122 } 123 string MsgSigature = ""; 124 int ret = 0; 125 ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature); 126 if (0 != ret) 127 return ret; 128 sEncryptMsg = ""; 129 130 string EncryptLabelHead = "<Encrypt><![CDATA["; 131 string EncryptLabelTail = "]]></Encrypt>"; 132 string MsgSigLabelHead = "<MsgSignature><![CDATA["; 133 string MsgSigLabelTail = "]]></MsgSignature>"; 134 string TimeStampLabelHead = "<TimeStamp><![CDATA["; 135 string TimeStampLabelTail = "]]></TimeStamp>"; 136 string NonceLabelHead = "<Nonce><![CDATA["; 137 string NonceLabelTail = "]]></Nonce>"; 138 sEncryptMsg = sEncryptMsg + "<xml>" + EncryptLabelHead + raw + EncryptLabelTail; 139 sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail; 140 sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail; 141 sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail; 142 sEncryptMsg += "</xml>"; 143 return 0; 144 } 145 146 public class DictionarySort : System.Collections.IComparer 147 { 148 public int Compare(object oLeft, object oRight) 149 { 150 string sLeft = oLeft as string; 151 string sRight = oRight as string; 152 int iLeftLength = sLeft.Length; 153 int iRightLength = sRight.Length; 154 int index = 0; 155 while (index < iLeftLength && index < iRightLength) 156 { 157 if (sLeft[index] < sRight[index]) 158 return -1; 159 else if (sLeft[index] > sRight[index]) 160 return 1; 161 else 162 index++; 163 } 164 return iLeftLength - iRightLength; 165 166 } 167 } 168 //Verify Signature 169 private static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture) 170 { 171 string hash = ""; 172 int ret = 0; 173 ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash); 174 if (ret != 0) 175 return ret; 176 //System.Console.WriteLine(hash); 177 if (hash == sSigture) 178 return 0; 179 else 180 { 181 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error; 182 } 183 } 184 185 public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, ref string sMsgSignature) 186 { 187 ArrayList AL = new ArrayList(); 188 AL.Add(sToken); 189 AL.Add(sTimeStamp); 190 AL.Add(sNonce); 191 AL.Add(sMsgEncrypt); 192 AL.Sort(new DictionarySort()); 193 string raw = ""; 194 for (int i = 0; i < AL.Count; ++i) 195 { 196 raw += AL[i]; 197 } 198 199 SHA1 sha; 200 ASCIIEncoding enc; 201 string hash = ""; 202 try 203 { 204 sha = new SHA1CryptoServiceProvider(); 205 enc = new ASCIIEncoding(); 206 byte[] dataToHash = enc.GetBytes(raw); 207 byte[] dataHashed = sha.ComputeHash(dataToHash); 208 hash = BitConverter.ToString(dataHashed).Replace("-", ""); 209 hash = hash.ToLower(); 210 } 211 catch (Exception) 212 { 213 return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error; 214 } 215 sMsgSignature = hash; 216 return 0; 217 } 218 } 219 }
1 using System; 2 using System.IO; 3 using System.Net; 4 using System.Security.Cryptography; 5 using System.Text; 6 7 namespace Core.Common.Wechat 8 { 9 /// <summary> 10 /// 11 /// </summary> 12 public class Cryptography 13 { 14 public static UInt32 HostToNetworkOrder(UInt32 inval) 15 { 16 UInt32 outval = 0; 17 for (int i = 0; i < 4; i++) 18 outval = (outval << 8) + ((inval >> (i * 8)) & 255); 19 return outval; 20 } 21 22 public static Int32 HostToNetworkOrder(Int32 inval) 23 { 24 Int32 outval = 0; 25 for (int i = 0; i < 4; i++) 26 outval = (outval << 8) + ((inval >> (i * 8)) & 255); 27 return outval; 28 } 29 /// <summary> 30 /// 解密方法 31 /// </summary> 32 /// <param name="Input">密文</param> 33 /// <param name="EncodingAESKey"></param> 34 /// <returns></returns> 35 /// 36 public static string AES_decrypt(String Input, string EncodingAESKey, ref string appid) 37 { 38 byte[] Key; 39 Key = Convert.FromBase64String(EncodingAESKey + "="); 40 byte[] Iv = new byte[16]; 41 Array.Copy(Key, Iv, 16); 42 byte[] btmpMsg = AES_decrypt(Input, Iv, Key); 43 44 int len = BitConverter.ToInt32(btmpMsg, 16); 45 len = IPAddress.NetworkToHostOrder(len); 46 47 48 byte[] bMsg = new byte[len]; 49 byte[] bAppid = new byte[btmpMsg.Length - 20 - len]; 50 Array.Copy(btmpMsg, 20, bMsg, 0, len); 51 Array.Copy(btmpMsg, 20 + len, bAppid, 0, btmpMsg.Length - 20 - len); 52 string oriMsg = Encoding.UTF8.GetString(bMsg); 53 appid = Encoding.UTF8.GetString(bAppid); 54 55 56 return oriMsg; 57 } 58 59 public static String AES_encrypt(String Input, string EncodingAESKey, string appid) 60 { 61 byte[] Key; 62 Key = Convert.FromBase64String(EncodingAESKey + "="); 63 byte[] Iv = new byte[16]; 64 Array.Copy(Key, Iv, 16); 65 string Randcode = CreateRandCode(16); 66 byte[] bRand = Encoding.UTF8.GetBytes(Randcode); 67 byte[] bAppid = Encoding.UTF8.GetBytes(appid); 68 byte[] btmpMsg = Encoding.UTF8.GetBytes(Input); 69 byte[] bMsgLen = BitConverter.GetBytes(HostToNetworkOrder(btmpMsg.Length)); 70 byte[] bMsg = new byte[bRand.Length + bMsgLen.Length + bAppid.Length + btmpMsg.Length]; 71 72 Array.Copy(bRand, bMsg, bRand.Length); 73 Array.Copy(bMsgLen, 0, bMsg, bRand.Length, bMsgLen.Length); 74 Array.Copy(btmpMsg, 0, bMsg, bRand.Length + bMsgLen.Length, btmpMsg.Length); 75 Array.Copy(bAppid, 0, bMsg, bRand.Length + bMsgLen.Length + btmpMsg.Length, bAppid.Length); 76 77 return AES_encrypt(bMsg, Iv, Key); 78 79 } 80 private static string CreateRandCode(int codeLen) 81 { 82 string codeSerial = "2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z"; 83 if (codeLen == 0) 84 { 85 codeLen = 16; 86 } 87 string[] arr = codeSerial.Split(','); 88 string code = ""; 89 int randValue = -1; 90 Random rand = new Random(unchecked((int)DateTime.Now.Ticks)); 91 for (int i = 0; i < codeLen; i++) 92 { 93 randValue = rand.Next(0, arr.Length - 1); 94 code += arr[randValue]; 95 } 96 return code; 97 } 98 99 private static String AES_encrypt(String Input, byte[] Iv, byte[] Key) 100 { 101 var aes = new RijndaelManaged(); 102 //秘鑰的大小,以位為單位 103 aes.KeySize = 256; 104 //支持的塊大小 105 aes.BlockSize = 128; 106 //填充模式 107 aes.Padding = PaddingMode.PKCS7; 108 aes.Mode = CipherMode.CBC; 109 aes.Key = Key; 110 aes.IV = Iv; 111 var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); 112 byte[] xBuff = null; 113 114 using (var ms = new MemoryStream()) 115 { 116 using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) 117 { 118 byte[] xXml = Encoding.UTF8.GetBytes(Input); 119 cs.Write(xXml, 0, xXml.Length); 120 } 121 xBuff = ms.ToArray(); 122 } 123 String Output = Convert.ToBase64String(xBuff); 124 return Output; 125 } 126 127 private static String AES_encrypt(byte[] Input, byte[] Iv, byte[] Key) 128 { 129 var aes = new RijndaelManaged(); 130 //秘鑰的大小,以位為單位 131