欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

微信公众号开发--用.Net Core实现微信消息加解密

程序员文章站 2022-04-18 22:33:23
1、进入微信公众号后台设置微信服务器配置参数(注意:Token和EncodingAESKey必须和微信服务器验证参数保持一致,不然验证不会通过)。 2、设置为安全模式 3、代码实现(主要分为验证接口和消息处理接口): 加解密实现(微信公众号官网有源码) ......

 

1、进入微信公众号后台设置微信服务器配置参数(注意:Token和EncodingAESKey必须和微信服务器验证参数保持一致,不然验证不会通过)。

微信公众号开发--用.Net Core实现微信消息加解密

2、设置为安全模式

微信公众号开发--用.Net Core实现微信消息加解密

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             aes.KeySize = 256;
132             //支持的块大小
133             aes.BlockSize = 128;
134             //填充模式
135             //aes.Padding = PaddingMode.PKCS7;
136             aes.Padding = PaddingMode.None;
137             aes.Mode = CipherMode.CBC;
138             aes.Key = Key;
139             aes.IV = Iv;
140             var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
141             byte[] xBuff = null;
142 
143             #region 自己进行PKCS7补位,用系统自己带的不行
144             byte[] msg = new byte[Input.Length + 32 - Input.Length % 32];
145             Array.Copy(Input, msg, Input.Length);
146             byte[] pad = KCS7Encoder(Input.Length);
147             Array.Copy(pad, 0, msg, Input.Length, pad.Length);
148             #endregion
149 
150             #region 注释的也是一种方法,效果一样
151             //ICryptoTransform transform = aes.CreateEncryptor();
152             //byte[] xBuff = transform.TransformFinalBlock(msg, 0, msg.Length);
153             #endregion
154 
155             using (var ms = new MemoryStream())
156             {
157                 using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
158                 {
159                     cs.Write(msg, 0, msg.Length);
160                 }
161                 xBuff = ms.ToArray();
162             }
163 
164             String Output = Convert.ToBase64String(xBuff);
165             return Output;
166         }
167 
168         private static byte[] KCS7Encoder(int text_length)
169         {
170             int block_size = 32;
171             // 计算需要填充的位数
172             int amount_to_pad = block_size - (text_length % block_size);
173             if (amount_to_pad == 0)
174             {
175                 amount_to_pad = block_size;
176             }
177             // 获得补位所用的字符
178             char pad_chr = chr(amount_to_pad);
179             string tmp = "";
180             for (int index = 0; index < amount_to_pad; index++)
181             {
182                 tmp += pad_chr;
183             }
184             return Encoding.UTF8.GetBytes(tmp);
185         }
186         /**
187          * 将数字转化成ASCII码对应的字符,用于对明文进行补码
188          * 
189          * @param a 需要转化的数字
190          * @return 转化得到的字符
191          */
192         static char chr(int a)
193         {
194 
195             byte target = (byte)(a & 0xFF);
196             return (char)target;
197         }
198         private static byte[] AES_decrypt(String Input, byte[] Iv, byte[] Key)
199         {
200             RijndaelManaged aes = new RijndaelManaged();
201             aes.KeySize = 256;
202             aes.BlockSize = 128;
203             aes.Mode = CipherMode.CBC;
204             aes.Padding = PaddingMode.None;
205             aes.Key = Key;
206             aes.IV = Iv;
207             var decrypt = aes.CreateDecryptor(aes.Key, aes.IV);
208             byte[] xBuff = null;
209             using (var ms = new MemoryStream())
210             {
211                 using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
212                 {
213                     byte[] xXml = Convert.FromBase64String(Input);
214                     byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32];
215                     Array.Copy(xXml, msg, xXml.Length);
216                     cs.Write(xXml, 0, xXml.Length);
217                 }
218                 xBuff = decode2(ms.ToArray());
219             }
220             return xBuff;
221         }
222         private static byte[] decode2(byte[] decrypted)
223         {
224             int pad = (int)decrypted[decrypted.Length - 1];
225             if (pad < 1 || pad > 32)
226             {
227                 pad = 0;
228             }
229             byte[] res = new byte[decrypted.Length - pad];
230             Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad);
231             return res;
232         }
233     }
234 }