zoukankan      html  css  js  c++  java
  • 微信公众号开发--用.Net Core实现微信消息加解密

    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             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 }
  • 相关阅读:
    45
    布里斯班初体验
    走出你的舒适区
    Homebrew- MAC上的包管理利器
    Java经典类库-Guava中的函数式编程讲解
    使用WebDriver遇到的那些坑
    CheckStyle, 强制你遵循编码规范
    利用php的register_shutdown_function来记录php的输出日志
    PHP-redis中文文档
    php中set_time_limit()函数运用
  • 原文地址:https://www.cnblogs.com/NuoYer/p/8251329.html
Copyright © 2011-2022 走看看