博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
微信公众号开发--用.Net Core实现微信消息加解密
阅读量:4515 次
发布时间:2019-06-08

本文共 19456 字,大约阅读时间需要 64 分钟。

 

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

2、设置为安全模式

3、代码实现(主要分为验证接口和消息处理接口):

1 ///  2 /// 验证接口 3 ///  4 /// 签名 5 /// 时间戳 6 ///  7 ///  8 /// 
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 /// 21 /// 接收消息并处理和返回相应结果22 /// 23 /// 当加密模式时才会有该变量(消息签名)24 /// 签名25 /// 时间戳26 /// 27 ///
28 [HttpPost, Route("Message")]29 [AllowAnonymous]30 public ActionResult MessagePost(string msg_signature, string signature, string timestamp, string nonce)31 {32 try33 {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参数的timestamp104         // @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce105         // @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             try116             {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 = "
";132 string MsgSigLabelHead = "
";134 string TimeStampLabelHead = "
";136 string NonceLabelHead = "
";138 sEncryptMsg = sEncryptMsg + "
" + EncryptLabelHead + raw + EncryptLabelTail;139 sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail;140 sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail;141 sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail;142 sEncryptMsg += "
";143 return 0;144 }145 146 public class DictionarySort : System.Collections.IComparer147 {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 else162 index++;163 }164 return iLeftLength - iRightLength;165 166 }167 }168 //Verify Signature169 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 else180 {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 try203 {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     ///  10     ///  11     ///  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         ///  30         /// 解密方法 31         ///  32         /// 密文 33         ///  34         /// 
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 #endregion149 150 #region 注释的也是一种方法,效果一样151 //ICryptoTransform transform = aes.CreateEncryptor();152 //byte[] xBuff = transform.TransformFinalBlock(msg, 0, msg.Length);153 #endregion154 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 }

 

转载于:https://www.cnblogs.com/NuoYer/p/8251329.html

你可能感兴趣的文章
配置phpstudy+phpstorm+xdebug环境
查看>>
BZOJ 1079 [SCOI2008]着色方案
查看>>
[Win8.1系统]双系统
查看>>
HDU 3899 树形DP
查看>>
继承上机作业
查看>>
设计模式 4/23 建造者模式
查看>>
Logging in Java
查看>>
leetcode算法:Distribute Candies
查看>>
机器学习之路: python 朴素贝叶斯分类器 MultinomialNB 预测新闻类别
查看>>
LINUX 忘记root密码
查看>>
json转换成Map
查看>>
MySQL查看当前用户、存储引擎、日志
查看>>
tpcc-mysql 系列二:进行TPCC测试
查看>>
将16进制的颜色值变成UIColor
查看>>
[转]magento 2 modes – 每种模式的特点及如何切换(翻译)
查看>>
求n的阶乘【VB代码实现】
查看>>
VSCode(Visual Studio Code) 自用插件
查看>>
NOIp2016纪录[那些我所追求的]
查看>>
(VB)定时更换(IE)代理IP(代理轮换)
查看>>
Node.js HTTP Server对象及GET、POST请求
查看>>