公众号消息加解密机制:开发者应注意的要点
公众号消息加解密是公众平台为了进一步强化公众号的安全保障而提供的一种新机制。开发者需要留意,公众账号主动调用 API 的这种情况不会受到影响。仅仅在被动回复用户消息的时候,才需要去进行消息加解密。消息加解密的具体修改包含以下方面:

新增了消息体签名验证,此验证用于公众平台和公众账号来验证消息体的正确性。对于推送给微信公众账号的普通消息和事件消息,还有推送给设备公众账号的设备消息,都进行了加密处理。同时,公众账号对密文消息的回复也需要进行加密。
开发者需要查看接入指引以及开发者 FAQ,以此来接入消息体签名及加解密功能。
启用加解密功能(也就是选择兼容模式或者安全模式之后),公众平台服务器在向公众账号服务器配置地址(能够在“开发者中心”进行修改)推送消息时,URL会新增两个参数(加密类型和消息体签名),通过这些来体现新功能。加密算法是 AES,关于具体的加解密流程和方案,可查看接入指引、技术方案和示例代码。
公众平台为配合消息加密功能上线并帮助开发者适配新特性,提供了 3 种加解密模式供开发者选择,分别是明文模式、兼容模式、安全模式。这 3 种模式可在“开发者中心”进行选择相应模式。在选择兼容模式和安全模式之前,需要在开发者中心填写消息加解密密钥 EncodingAESKey。
安全模式(推荐):公众平台发送的消息体内容仅含密文,公众账号回复的消息体也为密文,建议开发者调试成功后使用此模式收发消息。
什么是EncodingAESKey?
微信公众平台会使用 AES 对称加密算法来对推送给公众账号的消息体进行加密。而 EncodingAESKey 是用于加密的密钥。公众账号借助这个密钥来对接收到的密文消息体进行解密操作,并且回复的消息体也会使用这个密钥进行加密。
此外,微信公众平台给开发者提供了示例代码,且这些示例代码有 5 种语言。其中包括 C++版本、php 版本、Java 版本、Python 版本和 C#版本,可点击进行下载。
接入指引的消息解密方式是明文模式。假设 URL 被配置为某种情况,数据格式为 JSON,并且 Token 为 "AAAAA"。推送的 URL 链接是……推送的包体是……
{
"ToUserName": "gh_97417a04a28d",
"FromUserName": 以下是改写后的句子:“o9AgO5Kd5ggOC 与 bXrbNODIiE3bGY”,
"CreateTime": 1714037059,
"MsgType": "event",
"Event": "debug_demo",
"debug_str": "hello world"
}
校验签名是否为正确的 signature,以此来判断请求是否是由微信服务器发出的。先将 token、timestamp(URL 参数中的)、nonce(URL 参数中的)这三个参数按字典序进行排序,排序后的结果是["1714037059","486452656","AAAAA"]。接着把这三个参数字符串拼接成一个字符串,即"1714037059486452656AAAAA"。然后对这个拼接后的字符串进行 sha1 计算签名,得到的签名是 899cf89e464efb63f54ddac96b0a0a235f53aa78。最后将这个计算出的签名与 URL 链接中的 signature 参数进行对比,若两者相等,就说明请求是来自微信服务器的,是合法的。将回包发送给微信,回包的具体内容依据特定的接口文档来确定。倘若没有特定的要求,那么就回复空串或者“success”。消息解密方式采用安全模式。假设 URL 有相应配置,数据格式为 JSON,其中 Token 为“AAAAA”,EncodingAESKey 为“AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA”,公众号 Appid 为“wxba5fad812f8e6fb9”。 推送的URL链接:: 推送的包体:
{
"ToUserName": "gh_97417a04a28d",
"Encrypt": 无法确定具体含义,这段内容看起来像是一段加密或编码后的数据,没有明确的语义和可改写的句子结构。如果这是在特定的上下文中出现的,需要更多背景信息才能进行准确的改写。
}
校验 msg_signature 的签名是否正确,以此来判断请求是否源自微信服务器。需注意,不可使用 signature 进行验证!对消息体“Encrypt”的密文进行解密。AESKey 是通过将 EncodingAESKey 尾部填充一个字符的 "=" 后,再用 Base64_Decode 生成的,其长度为 32 个字节;将 Encrypt 密文进行 Base64 解码后得到 TmpMsg,其字节长度为 224;用 AESKey 对 TmpMsg 进行 AES 解密,得到的 FullStr 字节长度为 205。[id_1157360753][id_908891645]下载 FullStr 由 random(16B)、msg_len(4B)以及 msg 和 appid 组成,其中:在这个示例里,要回包给微信服务器,首先得确定回包包体的明文内容,这具体是由特定接口文档的要求来决定的,如果没有特定要求,回复空串或者 success(无需加密)就可以,而其他回包内容则需要进行加密处理。假设回包包体的明文内容是"{"demo_resp":"good luck"}",且数据格式为 JSON。接下来介绍对回包进行加密的方法:回包格式如下,它具体取决于你所配置的数据格式(JSON 或 XML),其中:
{
"Encrypt": "${msg_encrypt}$",
"MsgSignature": "${msg_signature}$",
"TimeStamp": ${timestamp}$,
"Nonce": ${nonce}$
}
<xml>
<Encrypt>Encrypt>
<MsgSignature>MsgSignature>
<TimeStamp>${timestamp}$TimeStamp>
<Nonce>Nonce>
xml>
生成 Encrypt 的方法如下:首先,将 EncodingAESKey 尾部填充一个字符的 "=",然后通过 Base64_Decode 生成 32 个字节的 AESKey,即 AESKey = Base64_Decode( EncodingAESKey + "=" );接着构造 FullStr = random(16B) + msg_len(4B) + msg + appid;在此示例中,用 AESKey 对 FullStr 进行加密,从而得到字节大小为 64 的 TmpMsg。AES 运用 CBC 模式,其秘钥长度为 32 个字节,也就是 256 位。在数据处理方面,采用 PKCS#7 填充方式。PKCS#7 规定,若 K 表示秘钥的字节数且为 32,Buf 是待加密的内容,N 是 Buf 的字节数,那么 Buf 需要被填充为 K 的整数倍。在 Buf 的尾部填充一些字节,这些字节的数量是(K - N%K),且每个字节的内容也都是(K - N%K)。微信团队给出了多种语言的示例代码,比如 PHP、Java、C++、Python、C#等,建议开发者尽量参照这些示例代码,在认真阅读技术文档、示例代码以及它们的注释之后,再开始进行编码调试工作。将TmpMsg进行Base64编码后,得到的Encrypt为"ELGduP2YcVatjqIS+eZbp80MNLoAUWvzzyJxgGzxZO/5sAvd070Bs6qrLARC9nVHm48Y4hyRbtzve1L32tmxSQ=="。开发者生成 TimeStamp,使用当前时间戳就行,比如使用 1713424427。把 Nonce 回填到 URL 参数中的 nonce 参数处就可以,例如使用 415670741。生成 MsgSignature 的方法如下:先将 token、TimeStamp(回包中的)、Nonce(回包中的)、Encrypt(回包中的)这四个参数进行字典序排序,排序后的结果为["1713424427","415670741","AAAAA","ELGduP2YcVatjqIS+eZbp80MNLoAUWvzzyJxgGzxZO/5sAvd070Bs6qrLARC9nVHm48Y4hyRbtzve1L32tmxSQ=="];接着把这四个参数字符串拼接成一个字符串,然后对其进行 sha1 计算签名,得到的签名为 1b9339964ed2e271e7c7b6ff2b0ef902fc94dea1;最终的回包就是这样。
{
"Encrypt": "ELGduP2YcVatjqIS+eZbp80MNLoAUWvzzyJxgGzxZO/5sAvd070Bs6qrLARC9nVHm48Y4hyRbtzve1L32tmxSQ 等于这个字符串。",
"MsgSignature": 该字符串为"1b9339964ed2e271e7c7b6ff2b0ef902fc94dea1",
"TimeStamp": 1713424427,
"Nonce": "415670741"
}
为了方便开发者进行调试,我们提供了一些相关的调试工具。其中包括请求构造工具和其他调试工具,供开发者使用。