消息加解密接入指引

开发者在代替授权公众号接收和处理消息时,出于安全考虑,必须对消息收发的过程进行必须的加解密。该文档讲述如何使用示例代码接入加解密,参考本文档并使用示例代码,加解密的接入将非常简单。若想进一步的了解细节,请查看 《公众号第三方平台的加密解密技术方案》

首先请注意,开发者在接收消息和事件时,都需要进行消息加解密(某些事件可能需要回复,回复时也需要先进行加密)。但是,通过API主动调用接口(包括调用客服消息接口发消息)时,不需要进行加密。

消息类型

公众号第三方平台可能会接收到两种类型的消息:

1、用户发送给公众号的消息(由公众号第三方平台代收)。此时,消息XML体中,ToUserName(即接收者)为公众号的原始ID(可通过《接口说明》中的获取授权方信息接口来获得)。

2、微信服务器发送给服务自身的事件推送(如取消授权通知,Ticket推送等)。此时,消息XML体中没有ToUserName字段,而是AppId字段,即公众号服务的AppId。这种系统事件推送通知(现在包括推送component_verify_ticket协议和推送取消授权通知),服务开发者收到后也需进行解密,接收到后只需直接返回字符串“success”。

开发者可以在微信公众平台接口调试工具,在接口类型中选择“消息接口调试”,并选择安全模式的加密调试,进行消息加解密的在线调试。

具体消息加解密的做法是,当关注者与已授权公众号进行交互时,公众号第三方平台将接收到相应的消息推送、事件推送。为了加强安全性,将对此过程进行2个措施:

1、在接收已授权公众号消息和事件的URL中,增加2个参数(此前已有2个参数,为时间戳 timestamp,随机数nonce),分别是encrypt_type(加密类型,为aes)和msg_signature(消息体签名,用于验证消息体的正确性)

2、postdata中的XML体,将使用第三方平台申请时的接收消息的加密symmetric_key(也称为EncodingAESKey)来进行加密。

示例代码

微信公众平台提供了c++, php, java, python, c# 5种语言的示例代码(点击下载,请运行示例代码前先阅读对应的readme文件),每种语言的类名和接口名均一致,下面以C++为例说明。

1.函数说明

1.1. 构造函数

// @param sToken: 第三方平台申请时填写的接收消息的校验token
// @param sEncodingAESKey: 第三方平台申请时填写的接收消息的加密symmetric_key
// @param sAppid: 公众号第三方平台的appidWXBizMsgCrypt(const std::string &sToken,
const std::string &sEncodingAESKey,
const std::string &sAppid)

1.2. 解密函数

// 检验消息的真实性,并且获取解密后的明文
// @param sMsgSignature: 签名串,对应URL参数的msg_signature
// @param sTimeStamp: 时间戳,对应URL参数的timestamp
// @param sNonce: 随机串,对应URL参数的nonce
// @param sPostData: 密文,对应POST请求的数据
// @param sMsg: 解密后的明文,当return返回0时有效
// @return: 成功0,失败返回对应的错误码
int DecryptMsg(const std::string &sMsgSignature,
const std::string &sTimeStamp,
const std::string &sNonce,
const std::string &sPostData,
std::string &sMsg);

1.3. 加密函数

//将公众号回复用户的消息加密打包
// @param sReplyMsg:公众号待回复用户的消息,xml格式的字符串
// @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp
// @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce
// @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce,encrypt的xml格式的字符串,当return返回0时有效
// return:成功0,失败返回对应的错误码
int EncryptMsg(const std::string &sReplyMsg,
const std::string &sTimeStamp,
const std::string &sNonce,
std::string &sEncryptMsg);

2. 使用方法

2.1 实例化对象

使用构造函数,实例化一个对象,传入公众号第三方平台的token(申请公众号第三方平台时填写的接收消息的校验token), 公众号第三方平台的appid, 公众号第三方平台的 EncodingAESKey(申请公众号第三方平台时填写的接收消息的加密symmetric_key

2.2 解密
安全模式下,第三方平台方收到以下带密文消息体:

encrypt_msg =
<xml>
<ToUserName></ToUserName>
<Encrypt></Encrypt>
</xml>

调用示例代码中的DecryptMsg函数(需传入msg_signature、timetamp、noncepostdata,前3个参数可从接收已授权公众号消息和事件的URL中获得,postdata即为POST过来的数据包内容),若调用成功,sMsg则为输出结果,其内容为如下的明文的xml消息体:

<xml>
<ToUserName></ToUserName>
<FromUserName></FromUserName>
<CreateTime>1411035097</CreateTime>
<MsgType></MsgType>
<Content></Content>
<MsgId>6060349595123187712</MsgId>
</xml>

2.3 公众帐号处理消息,生成需要回复给微信公众平台的xml消息体,假设回复以下内容:

res_msg =
<xml>
<ToUserName></ToUserName>
<FromUserName></FromUserName>
<CreateTime>1411034505</CreateTime>
<MsgType></MsgType>
<Content></Content>
<FuncFlag>0</FuncFlag>
</xml>

2.4 回包加密

调用EncryptMsg接口,传入需要回复给微信公众平台的res_msg, timestamp, nonce, 若加密成功,则sEncryptMsg为密文消息体,内容如下:

<xml>
<Encrypt>
<![CDATA[LDFAmKFr7U/RMmwRbsR676wjym90byw7+hhh226e8bu6KVYy00HheIsVER4eMgz/VBtofSaeXXQBz6fVdkN2CzBUaTtjJeTCXEIDfTBNxpw/QRLGLq
qMZHA3I+JiBxrrSzd2yXuXst7TdkVgY4lZEHQcWk85x1niT79XLaWQog+OnBV31eZbXGPPv8dZciKqGo0meTYi+fkMEJdyS8OE7NjO79vpIyIw7hMBtEXPBK/tJGN5m5SoAS
6I4rRZ8Zl8umKxXqgr7N8ZOs6DB9tokpvSl9wT9T3E62rufaKP5EL1imJUd1pngxy09EP24O8Th4bCrdUcZpJio2l11vE6bWK2s5WrLuO0cKY2GP2unQ4fDxh0L4ePmNOVFJ
wp9Hyvd0BAsleXA4jWeOMw5nH3Vn49/Q/ZAQ2HN3dB0bMA+6KJYLvIzTz/Iz6vEjk8ZkK+AbhW5eldnyRDXP/OWfZH2P3WQZUwc/G/LGmS3ekqMwQThhS2Eg5t4yHv0mAIei
07Lknip8nnwgEeF4R9hOGutE9ETsGG4CP1LHTQ4fgYchOMfB3wANOjIt9xendbhHbu51Z4OKnA0F+MlgZomiqweT1v/+LUxcsFAZ1J+Vtt0FQXElDKg+YyQnRCiLl3I+GJ/c
xSj86XwClZC3NNhAkVU11SvxcXEYh9smckV/qRP2Acsvdls0UqZVWnPtzgx8hc8QBZaeH+JeiaPQD88frNvA==]]> </Encrypt>
<MsgSignature></MsgSignature>
<TimeStamp>1411034505</TimeStamp>
<Nonce></Nonce>
</xml>

3. 注意事项

1.EncodingAESKey长度固定为43个字符,从a-z,A-Z,0-9共62个字符中选取。

2.出于安全考虑,开放平台网站提供了修改EncodingAESKey的功能(在EncodingAESKey可能泄漏时进行修改),所以建议公众账号保存当前的和上一次的EncodingAESKey,若当前EncodingAESKey解密失败,则尝试用上一次的EncodingAESKey的解密。回包时,用哪个Key解密成功,则用此Key加密对应的回包。

消息解密流程图:


微信官方示例

注意事项

  1. Cryptography.cs文件封装了AES加解密过程,用户无须关心具体实现。WXBizMsgCrypt.cs文件提供了用户接入企业微信的两个接口,Sample.cs文件提供了如何使用这两个接口的示例。
  2. WXBizMsgCrypt.cs封装了DecryptMsg, EncryptMsg两个接口,分别用于收到用户回复消息的解密以及开发者回复消息的加密过程。使用方法可以参考Sample.cs文件。
  3. 加解密协议请参考微信公众平台官方文档。

Sample.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace MsgCryptTest
{class Sample{static void Main(string[] args){//公众平台上开发者设置的token, appID, EncodingAESKeystring sToken = "QDG6eK";string sAppID = "wx5823bf96d3bd56c7";string sEncodingAESKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C";Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(sToken, sEncodingAESKey, sAppID);/* 1. 对用户回复的数据进行解密。* 用户回复消息或者点击事件响应时,企业会收到回调消息,假设企业收到的推送消息:*    POST /cgi-bin/wxpush? msg_signature=477715d11cdb4164915debcba66cb864d751f3e6&timestamp=1409659813&nonce=1372623149 HTTP/1.1Host: qy.weixin.qq.comContent-Length: 613**   <xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName><Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt></xml>*/string sReqMsgSig = "477715d11cdb4164915debcba66cb864d751f3e6";string sReqTimeStamp = "1409659813";string sReqNonce = "1372623149";string sReqData = "<xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName><Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt></xml>";string sMsg = "";  //解析之后的明文int ret = 0;ret = wxcpt.DecryptMsg(sReqMsgSig, sReqTimeStamp, sReqNonce, sReqData, ref sMsg);if (ret != 0){System.Console.WriteLine("ERR: Decrypt fail, ret: " + ret);return;}System.Console.WriteLine(sMsg);/** 2. 企业回复用户消息也需要加密和拼接xml字符串。* 假设企业需要回复用户的消息为:*       <xml>*        <ToUserName><![CDATA[mycreate]]></ToUserName>*        <FromUserName><![CDATA[wx5823bf96d3bd56c7]]></FromUserName>*      <CreateTime>1348831860</CreateTime><MsgType><![CDATA[text]]></MsgType>*      <Content><![CDATA[this is a test]]></Content>*      <MsgId>1234567890123456</MsgId>*      </xml>* 生成xml格式的加密消息过程为:*/string sRespData = "<xml><ToUserName><![CDATA[mycreate]]></ToUserName><FromUserName><![CDATA[wx582测试一下中文的情况,消息长度是按字节来算的396d3bd56c7]]></FromUserName><CreateTime>1348831860</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[this is a test]]></Content><MsgId>1234567890123456</MsgId></xml>";string sEncryptMsg = ""; //xml格式的密文ret = wxcpt.EncryptMsg(sRespData, sReqTimeStamp, sReqNonce, ref sEncryptMsg);System.Console.WriteLine("sEncryptMsg");System.Console.WriteLine(sEncryptMsg);/*测试:* 将sEncryptMsg解密看看是否是原文* */XmlDocument doc = new XmlDocument();doc.LoadXml(sEncryptMsg);XmlNode root = doc.FirstChild;string sig = root["MsgSignature"].InnerText;string enc = root["Encrypt"].InnerText;string timestamp = root["TimeStamp"].InnerText;string nonce = root["Nonce"].InnerText;string stmp = "";ret = wxcpt.DecryptMsg(sig, timestamp, nonce, sEncryptMsg, ref stmp);System.Console.WriteLine("stemp");System.Console.WriteLine(stmp + ret);return;}}
}

WXBizMsgCrypt.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Collections;
//using System.Web;
using System.Security.Cryptography;
//-40001 : 签名验证错误
//-40002 :  xml解析失败
//-40003 :  sha加密生成签名失败
//-40004 :  AESKey 非法
//-40005 :  appid 校验错误
//-40006 :  AES 加密失败
//-40007 : AES 解密失败
//-40008 : 解密后得到的buffer非法
//-40009 :  base64加密异常
//-40010 :  base64解密异常
namespace Tencent
{class WXBizMsgCrypt{string m_sToken;string m_sEncodingAESKey;string m_sAppID;enum WXBizMsgCryptErrorCode{WXBizMsgCrypt_OK = 0,WXBizMsgCrypt_ValidateSignature_Error = -40001,WXBizMsgCrypt_ParseXml_Error = -40002,WXBizMsgCrypt_ComputeSignature_Error = -40003,WXBizMsgCrypt_IllegalAesKey = -40004,WXBizMsgCrypt_ValidateAppid_Error = -40005,WXBizMsgCrypt_EncryptAES_Error = -40006,WXBizMsgCrypt_DecryptAES_Error = -40007,WXBizMsgCrypt_IllegalBuffer = -40008,WXBizMsgCrypt_EncodeBase64_Error = -40009,WXBizMsgCrypt_DecodeBase64_Error = -40010};//构造函数// @param sToken: 公众平台上,开发者设置的Token// @param sEncodingAESKey: 公众平台上,开发者设置的EncodingAESKey// @param sAppID: 公众帐号的appidpublic WXBizMsgCrypt(string sToken, string sEncodingAESKey, string sAppID){m_sToken = sToken;m_sAppID = sAppID;m_sEncodingAESKey = sEncodingAESKey;}// 检验消息的真实性,并且获取解密后的明文// @param sMsgSignature: 签名串,对应URL参数的msg_signature// @param sTimeStamp: 时间戳,对应URL参数的timestamp// @param sNonce: 随机串,对应URL参数的nonce// @param sPostData: 密文,对应POST请求的数据// @param sMsg: 解密后的原文,当return返回0时有效// @return: 成功0,失败返回对应的错误码public int DecryptMsg(string sMsgSignature, string sTimeStamp, string sNonce, string sPostData, ref string sMsg){if (m_sEncodingAESKey.Length!=43){return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;}XmlDocument doc = new XmlDocument();XmlNode root;string sEncryptMsg;try{doc.LoadXml(sPostData);root = doc.FirstChild;sEncryptMsg = root["Encrypt"].InnerText;}catch (Exception){return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ParseXml_Error;}//verify signatureint ret = 0;ret = VerifySignature(m_sToken, sTimeStamp, sNonce, sEncryptMsg, sMsgSignature);if (ret != 0)return ret;//decryptstring cpid = "";try{sMsg = Cryptography.AES_decrypt(sEncryptMsg, m_sEncodingAESKey, ref cpid);}catch (FormatException){return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecodeBase64_Error;}catch (Exception){return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_DecryptAES_Error;}if (cpid != m_sAppID)return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateAppid_Error;return 0;}//将企业号回复用户的消息加密打包// @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串// @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp// @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce// @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,//                       当return返回0时有效// return:成功0,失败返回对应的错误码public int EncryptMsg(string sReplyMsg, string sTimeStamp, string sNonce, ref string sEncryptMsg){if (m_sEncodingAESKey.Length!=43){return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_IllegalAesKey;}string raw = "";try{raw = Cryptography.AES_encrypt(sReplyMsg, m_sEncodingAESKey, m_sAppID);}catch (Exception){return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_EncryptAES_Error;}string MsgSigature = "";int ret = 0;ret = GenarateSinature(m_sToken, sTimeStamp, sNonce, raw, ref MsgSigature);if (0 != ret)return ret;sEncryptMsg = "";string EncryptLabelHead = "<Encrypt><![CDATA[";string EncryptLabelTail = "]]></Encrypt>";string MsgSigLabelHead = "<MsgSignature><![CDATA[";string MsgSigLabelTail = "]]></MsgSignature>";string TimeStampLabelHead = "<TimeStamp><![CDATA[";string TimeStampLabelTail = "]]></TimeStamp>";string NonceLabelHead = "<Nonce><![CDATA[";string NonceLabelTail = "]]></Nonce>";sEncryptMsg = sEncryptMsg + "<xml>" + EncryptLabelHead + raw + EncryptLabelTail;sEncryptMsg = sEncryptMsg + MsgSigLabelHead + MsgSigature + MsgSigLabelTail;sEncryptMsg = sEncryptMsg + TimeStampLabelHead + sTimeStamp + TimeStampLabelTail;sEncryptMsg = sEncryptMsg + NonceLabelHead + sNonce + NonceLabelTail;sEncryptMsg += "</xml>";return 0;}public class DictionarySort : System.Collections.IComparer{public int Compare(object oLeft, object oRight){string sLeft = oLeft as string;string sRight = oRight as string;int iLeftLength = sLeft.Length;int iRightLength = sRight.Length;int index = 0;while (index < iLeftLength && index < iRightLength){if (sLeft[index] < sRight[index])return -1;else if (sLeft[index] > sRight[index])return 1;elseindex++;}return iLeftLength - iRightLength;}}//Verify Signatureprivate static int VerifySignature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt, string sSigture){string hash = "";int ret = 0;ret = GenarateSinature(sToken, sTimeStamp, sNonce, sMsgEncrypt, ref hash);if (ret != 0)return ret;//System.Console.WriteLine(hash);if (hash == sSigture)return 0;else{return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ValidateSignature_Error;}}public static int GenarateSinature(string sToken, string sTimeStamp, string sNonce, string sMsgEncrypt ,ref string sMsgSignature){ArrayList AL = new ArrayList();AL.Add(sToken);AL.Add(sTimeStamp);AL.Add(sNonce);AL.Add(sMsgEncrypt);AL.Sort(new DictionarySort());string raw = "";for (int i = 0; i < AL.Count; ++i){raw += AL[i];}SHA1 sha;ASCIIEncoding enc;string hash = "";try{sha = new SHA1CryptoServiceProvider();enc = new ASCIIEncoding();byte[] dataToHash = enc.GetBytes(raw);byte[] dataHashed = sha.ComputeHash(dataToHash);hash = BitConverter.ToString(dataHashed).Replace("-", "");hash = hash.ToLower();}catch (Exception){return (int)WXBizMsgCryptErrorCode.WXBizMsgCrypt_ComputeSignature_Error;}sMsgSignature = hash;return 0;}}
}

Cryptography.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Net;
namespace Tencent
{class Cryptography{public static UInt32 HostToNetworkOrder(UInt32 inval){UInt32 outval = 0;for (int i = 0; i < 4; i++)outval = (outval << 8) + ((inval >> (i * 8)) & 255);return outval;}public static Int32 HostToNetworkOrder(Int32 inval){Int32 outval = 0;for (int i = 0; i < 4; i++)outval = (outval << 8) + ((inval >> (i * 8)) & 255);return outval;}/// <summary>/// 解密方法/// </summary>/// <param name="Input">密文</param>/// <param name="EncodingAESKey"></param>/// <returns></returns>/// public static string AES_decrypt(String Input, string EncodingAESKey, ref string appid){byte[] Key;Key = Convert.FromBase64String(EncodingAESKey + "=");byte[] Iv = new byte[16];Array.Copy(Key, Iv, 16);byte[] btmpMsg = AES_decrypt(Input, Iv, Key);int len = BitConverter.ToInt32(btmpMsg, 16);len = IPAddress.NetworkToHostOrder(len);byte[] bMsg = new byte[len];byte[] bAppid = new byte[btmpMsg.Length - 20 - len];Array.Copy(btmpMsg, 20, bMsg, 0, len);Array.Copy(btmpMsg, 20+len , bAppid, 0, btmpMsg.Length - 20 - len);string oriMsg = Encoding.UTF8.GetString(bMsg);appid = Encoding.UTF8.GetString(bAppid);return oriMsg;}public static String AES_encrypt(String Input, string EncodingAESKey, string appid){byte[] Key;Key = Convert.FromBase64String(EncodingAESKey + "=");byte[] Iv = new byte[16];Array.Copy(Key, Iv, 16);string Randcode = CreateRandCode(16);byte[] bRand = Encoding.UTF8.GetBytes(Randcode);byte[] bAppid = Encoding.UTF8.GetBytes(appid);byte[] btmpMsg = Encoding.UTF8.GetBytes(Input);byte[] bMsgLen = BitConverter.GetBytes(HostToNetworkOrder(btmpMsg.Length));byte[] bMsg = new byte[bRand.Length + bMsgLen.Length + bAppid.Length + btmpMsg.Length];Array.Copy(bRand, bMsg, bRand.Length);Array.Copy(bMsgLen, 0, bMsg, bRand.Length, bMsgLen.Length);Array.Copy(btmpMsg, 0, bMsg, bRand.Length + bMsgLen.Length, btmpMsg.Length);Array.Copy(bAppid, 0, bMsg, bRand.Length + bMsgLen.Length + btmpMsg.Length, bAppid.Length);return AES_encrypt(bMsg, Iv, Key);}private static string CreateRandCode(int codeLen){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";if (codeLen == 0){codeLen = 16;}string[] arr = codeSerial.Split(',');string code = "";int randValue = -1;Random rand = new Random(unchecked((int)DateTime.Now.Ticks));for (int i = 0; i < codeLen; i++){randValue = rand.Next(0, arr.Length - 1);code += arr[randValue];}return code;}private static String AES_encrypt(String Input, byte[] Iv, byte[] Key){var aes = new RijndaelManaged();//秘钥的大小,以位为单位aes.KeySize = 256;//支持的块大小aes.BlockSize = 128;//填充模式aes.Padding = PaddingMode.PKCS7;aes.Mode = CipherMode.CBC;aes.Key = Key;aes.IV = Iv;var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);byte[] xBuff = null;using (var ms = new MemoryStream()){using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)){byte[] xXml = Encoding.UTF8.GetBytes(Input);cs.Write(xXml, 0, xXml.Length);}xBuff = ms.ToArray();}String Output = Convert.ToBase64String(xBuff);return Output;}private static String AES_encrypt(byte[] Input, byte[] Iv, byte[] Key){var aes = new RijndaelManaged();//秘钥的大小,以位为单位aes.KeySize = 256;//支持的块大小aes.BlockSize = 128;//填充模式//aes.Padding = PaddingMode.PKCS7;aes.Padding = PaddingMode.None;aes.Mode = CipherMode.CBC;aes.Key = Key;aes.IV = Iv;var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);byte[] xBuff = null;#region 自己进行PKCS7补位,用系统自己带的不行byte[] msg = new byte[Input.Length + 32 - Input.Length % 32];Array.Copy(Input, msg, Input.Length);byte[] pad = KCS7Encoder(Input.Length);Array.Copy(pad, 0, msg, Input.Length, pad.Length);#endregion#region 注释的也是一种方法,效果一样//ICryptoTransform transform = aes.CreateEncryptor();//byte[] xBuff = transform.TransformFinalBlock(msg, 0, msg.Length);#endregionusing (var ms = new MemoryStream()){using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)){cs.Write(msg, 0, msg.Length);}xBuff = ms.ToArray();}String Output = Convert.ToBase64String(xBuff);return Output;}private static byte[] KCS7Encoder(int text_length){int block_size = 32;// 计算需要填充的位数int amount_to_pad = block_size - (text_length % block_size);if (amount_to_pad == 0){amount_to_pad = block_size;}// 获得补位所用的字符char pad_chr = chr(amount_to_pad);string tmp = "";for (int index = 0; index < amount_to_pad; index++){tmp += pad_chr;}return Encoding.UTF8.GetBytes(tmp);}/*** 将数字转化成ASCII码对应的字符,用于对明文进行补码* * @param a 需要转化的数字* @return 转化得到的字符*/static char chr(int a){byte target = (byte)(a & 0xFF);return (char)target;}private static byte[] AES_decrypt(String Input, byte[] Iv, byte[] Key){RijndaelManaged aes = new RijndaelManaged();aes.KeySize = 256;aes.BlockSize = 128;aes.Mode = CipherMode.CBC;aes.Padding = PaddingMode.None;aes.Key = Key;aes.IV = Iv;var decrypt = aes.CreateDecryptor(aes.Key, aes.IV);byte[] xBuff = null;using (var ms = new MemoryStream()){using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write)){byte[] xXml = Convert.FromBase64String(Input);byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32];Array.Copy(xXml, msg, xXml.Length);cs.Write(xXml, 0, xXml.Length);}xBuff = decode2(ms.ToArray());}return xBuff;}private static byte[] decode2(byte[] decrypted){int pad = (int)decrypted[decrypted.Length - 1];if (pad < 1 || pad > 32){pad = 0;}byte[] res = new byte[decrypted.Length - pad];Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad);return res;}}
}

微信开放平台(消息加解密接入指引)相关推荐

  1. 05. 微信公众号消息加解密

    1. 说明 官方文档 只有被动回复用户的消息时,才需要进行消息加解密 消息加解密的具体修改 新增消息体签名验证,用于公众平台和公众账号验证消息体的正确性 针对推送给微信公众账号的普通消息和事件消息,以 ...

  2. 多多客api_多多客小程序如何配置微信开放平台?

    原标题:多多客小程序如何配置微信开放平台? https://open.weixin.qq.com/ open: appid: xxx //微信开放平台AppID appsecret: xxx //微信 ...

  3. 微信开放平台-第三方平台-全网发布接入【java版本】

    微信给出的文档 概述 在第三方平台方创建成功并最终开发测试完毕,提交全网发布申请时,微信服务器会通过自动化测试的方式,检测服务的基础逻辑是否可用,在确保基础可用的情况下,才会允许公众号第三方平台提交全 ...

  4. 微信开放平台_第三方平台授权流程_验证票据

    一.验证票据 1.1 官方说明: 验证票据(component_verify_ticket):在第三方平台创建审核通过后,微信服务器会向其"授权事件接收URL" 每隔 10 分钟以 ...

  5. 微信开放平台【第三方平台】java开发总结:验证票据(component_verify_ticket)(-)

    微信第三方平台验证票据(component_verify_ticket)开发说明(全网最详细的微信第三方平台验证票据开发说明) 概述 微信公众平台-第三方平台(简称第三方平台)开放给所有通过开发者资质 ...

  6. 微信公众平台开发(9)--消息加解密方式

    点此查看 微信公众号/微信网页/微信支付/企业微信/小程序开发合集及源代码下载 本文目录 1. 前言 2. 判断加解密方式 3. 测试 4. 小结 1. 前言 登录微信公众平台,点击[设置与开发]-[ ...

  7. 微信公众平台消息体加解密实现

    一.消息体加解密 微信公众平台在配置服务器时,提供了3种加解密的模式供开发者选择,即明文模式.兼容模式.安全模式,选择兼容模式和安全模式前,需在开发者中心填写消息加解密密钥EncodingAESKey ...

  8. 微信第三方平台开发三(消息加解密)

    代码根本不需要自己写,微信提供示例代码,参数需要"公众号消息校验Token","公众号消息加解密Key" 我是用java写的,出现了下列问题 1.报错:java ...

  9. 微信信息如何在服务器加密,微信 消息加解密说明

    公众号消息加解密是公众平台为了进一步加强公众号安全保障,提供的新机制.开发者需注意,公众账号主动调用API的情况将不受影响.只有被动回复用户的消息时,才需要进行消息加解密.消息加解密的具体修改包括:1 ...

最新文章

  1. Spring Boot WebFlux 全局异常处理(404,500)解决IllegalArgumentException: Property 'message...
  2. S5700三层交换机 复杂网络环境设置默认路优先级实战
  3. 快速在PowerPoint文档中添加图表
  4. python语言格式化输出_Python字符串格式化输出
  5. spring boot自定义配置文件
  6. js 技巧杂引(转)
  7. linux将db2账户添加到组_超实用的shell脚本--Linux安全加固设置,值得收藏
  8. 三年级小孩近视150度需要戴近视眼镜么?
  9. 建立.NET Core控制台应用程序以输出EXE?
  10. Centos 7.2基础安装和配置(含分区方案建议)
  11. 第二次发布 - 轻剑傲风的RSS简易阅读器改装版。
  12. 360影视php采集接口,基于360影视采集实例
  13. 有关access的上机试题_2016年计算机二级《Access》上机操作题及答案
  14. sql自定义报表软件_SQL Server中的报表–自定义报表的外观
  15. “圆周率的计算”实例详解
  16. Android Settings 应用二 获取应用消耗的流量
  17. 手机邮箱中的邮件里的链接的打开方式/app更改方法
  18. 浅谈道路交通事故车辆安全技术鉴定
  19. JQuery入门级-基础
  20. 极值分布的类型及性质-----极值理论的学习1

热门文章

  1. YOLOv3庖丁解牛(一):网络结构
  2. 计算机英语教程哪个好,目前国内大学计算机英语教程教学情况分析
  3. 小小勇者服务器维护,小小勇者神秘商人刷新机制位置攻略
  4. CRM系统能给销售人员带来哪些帮助?以下关键别忽略
  5. [DirectX12学习笔记] 环境光遮蔽
  6. select * from dual (转)
  7. c#窗体应用计算机设计,C#应用程序设计
  8. uniapp 提供的手指事件
  9. 最根本的穴位-会阴穴
  10. 2.8 数值分析: 矩阵的范数