# 消息加解密说明

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

1）新增消息体签名验证，用于公众平台和公众号验证消息体的正确性

2）针对推送给微信公众号的普通消息和事件消息，以及推送给设备公众号的设备消息进行加密

3）公众号对密文消息的回复也要求加密

请开发者查看接入指引和开发者 FAQ 来接入消息体签名及加解密功能：接入指引

启用加解密功能（即选择兼容模式或安全模式）后，公众平台服务器在向公众号服务器配置地址（可在「[微信开发者平台](https://developers.weixin.qq.com/platform/) - 我的业务 - 公众号 - 消息与事件推送」处修改）推送消息时，URL 将新增加两个参数（加密类型和消息体签名），并以此来体现新功能。加密算法采用 AES，具体的加解密流程和方案请看接入指引、技术方案和示例代码。

![](https://testchu-7gy8occc8dcc14c3-1304825656.tcloudbaseapp.com/img%2Fmelody%2F%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_98143a94-d686-4f11-b7a3-74631d0aaa6b.png)

为了配合消息加密功能的上线，并帮助开发者适配新特性，公众平台提供了 3 种加解密的模式供开发者选择，即明文模式、兼容模式、安全模式（可在「开发者中心」选择相应模式），选择兼容模式和安全模式前，需在开发者中心填写消息加解密密钥 EncodingAESKey。

**明文模式**

- 维持现有模式，没有适配加解密新特性，消息体明文收发，默认设置为明文模式。

**兼容模式**

- 公众平台发送消息内容将同时包括明文和密文，消息包长度增加到原来的 3 倍左右；公众号回复明文或密文均可，不影响现有消息收发；开发者可在此模式下进行调试。

**安全模式（推荐）**

- 公众平台发送消息体的内容只含有密文，公众号回复的消息体也为密文，建议开发者在调试成功后使用此模式收发消息。

**EncodingAESKey 介绍**

微信公众平台采用 AES 对称加密算法对推送给公众号的消息体进行加密，EncodingAESKey 则是加密所用的秘钥。公众号用此秘钥对收到的密文消息体进行解密，回复消息体也用此秘钥加密。

此外，微信公众平台为开发者提供了 5 种语言的示例代码（包括 C++、PHP、Java、Python 和 C# 版本，[点击下载](https://wximg.gtimg.com/shake_tv/mpwiki/cryptoDemo.zip)）。

## 接入指引

### 消息解密方式为明文模式

1）假设 URL 配置为 `https://www.qq.com/revice`，数据格式为 JSON，Token="AAAAA"。

2）推送的 URL 链接：`https://www.qq.com/recive?signature=899cf89e464efb63f54ddac96b0a0a235f53aa78&timestamp=1714037059&nonce=486452656`

3）推送的包体：

```json
{
    "ToUserName": "gh_97417a04a28d",
    "FromUserName": "o9AgO5Kd5ggOC-bXrbNODIiE3bGY",
    "CreateTime": 1714037059,
    "MsgType": "event",
    "Event": "debug_demo",
    "debug_str": "hello world"
}
```

4）校验 `signature` 签名是否正确，以判断请求是否来自微信服务器。
  - 将 token、timestamp（URL 参数中的）、nonce（URL 参数中的）三个参数进行字典序排序，排序后结果为：`["1714037059","486452656","AAAAA"]`
  - 将三个参数字符串拼接成一个字符串：`"1714037059486452656AAAAA"`
  - 进行 sha1 计算签名：`899cf89e464efb63f54ddac96b0a0a235f53aa78`
  - 与 URL 链接中的 `signature` 参数进行对比，相等说明请求来自微信服务器，合法。

5）回包给微信，具体回包内容取决于特定接口文档要求，如无特定要求，回复空串或者 `success` 即可。

### 消息解密方式为安全模式

1）假设 URL 配置为 `https://www.qq.com/revice`，数据格式为 JSON，Token="AAAAA"，EncodingAESKey="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"，公众号 Appid="wxba5fad812f8e6fb9"。

2）推送的 URL 链接：`https://www.qq.com/recive?signature=6c5c811b55cc85e0e1b54100749188c20beb3f5d&timestamp=1714112445&nonce=415670741&openid=o9AgO5Kd5ggOC-bXrbNODIiE3bGY&encrypt_type=aes&msg_signature=046e02f8204d34f8ba5fa3b1db94908f3df2e9b3`

3）推送的包体：

```json
{
    "ToUserName": "gh_97417a04a28d",
    "Encrypt": "+qdx1OKCy+5JPCBFWw70tm0fJGb2Jmeia4FCB7kao+/Q5c/ohsOzQHi8khUOb05JCpj0JB4RvQMkUyus8TPxLKJGQqcvZqzDpVzazhZv6JsXUnnR8XGT740XgXZUXQ7vJVnAG+tE8NUd4yFyjPy7GgiaviNrlCTj+l5kdfMuFUPpRSrfMZuMcp3Fn2Pede2IuQrKEYwKSqFIZoNqJ4M8EajAsjLY2km32IIjdf8YL/P50F7mStwntrA2cPDrM1kb6mOcfBgRtWygb3VIYnSeOBrebufAlr7F9mFUPAJGj04="
}
```

4）校验 `msg_signature` 签名是否正确，以判断请求是否来自微信服务器。注意：不要使用 `signature` 验证！
  - 将 token、timestamp（URL 参数中的）、nonce（URL 参数中的）、Encrypt（包体内的字段）四个参数进行字典序排序
  - 将四个参数字符串拼接成一个字符串，然后进行 sha1 计算签名：`046e02f8204d34f8ba5fa3b1db94908f3df2e9b3`
  - 与 URL 参数中的 `msg_signature` 参数进行对比，相等说明请求来自微信服务器，合法。

5）解密消息体 `Encrypt` 密文。
  - AESKey = Base64_Decode(EncodingAESKey + "=")，EncodingAESKey 尾部填充一个字符的 "="，用 Base64_Decode 生成 32 个字节的 AESKey
  - 将 Encrypt 密文进行 Base64 解码，得到 TmpMsg，字节长度为 224
  - 将 TmpMsg 使用 AESKey 进行 AES 解密，得到 FullStr，字节长度为 205。AES 采用 CBC 模式，秘钥长度为 32 个字节（256 位），数据采用 PKCS#7 填充；PKCS#7：K 为秘钥字节数（采用 32），Buf 为待加密的内容，N 为其字节数。Buf 需要被填充为 K 的整数倍。在 Buf 的尾部填充(K - N%K)个字节，每个字节的内容是(K - N%K)。微信团队提供了多种语言的示例代码（包括 PHP、Java、C++、Python、C#），请开发者尽量使用示例代码，仔细阅读技术文档、示例代码及其注释后，再进行编码调试。[示例下载](https://wximg.gtimg.com/shake_tv/mpwiki/cryptoDemo.zip)
  - FullStr = random(16B) + msg_len(4B) + msg + appid，其中：
    - random(16B) 为 16 字节的随机字符串
    - msg_len 为 msg 长度，占 4 个字节(网络字节序)
    - msg 为解密后的明文
    - appid 为公众号 Appid，开发者需验证此 Appid 是否与自身公众号相符
  - 在此示例中：
    - random(16B) = "a8eedb185eb2fecf"
    - msg_len = 167（注意：需按网络字节序，占 4 个字节）
    - msg = `{"ToUserName":"gh_97417a04a28d","FromUserName":"o9AgO5Kd5ggOC-bXrbNODIiE3bGY","CreateTime":1714112445,"MsgType":"event","Event":"debug_demo","debug_str":"hello world"}`
    - appid = "wxba5fad812f8e6fb9"

6）回包给微信服务器，首先需确定回包包体的明文内容，具体取决于特定接口文档要求，如无特定要求，回复空串或者 `success`（无需加密）即可，其他回包内容需加密处理。这里假设回包包体的明文内容为 `{"demo_resp":"good luck"}`，数据格式为 JSON，下面介绍如何对回包进行加密：

7）回包格式如下，具体取决于你配置的数据格式（JSON 或 XML），其中：
  - Encrypt：加密后的内容
  - MsgSignature：签名，微信服务器会验证签名
  - TimeStamp：时间戳
  - Nonce：随机数

```json
{
    "Encrypt": "${msg_encrypt}$",
    "MsgSignature": "${msg_signature}$",
    "TimeStamp": ${timestamp}$,
    "Nonce": ${nonce}$
}
```

```xml
<xml>
    <Encrypt><![CDATA[${msg_encrypt}$]]></Encrypt>
    <MsgSignature><![CDATA[${msg_signature}$]]></MsgSignature>
    <TimeStamp>${timestamp}$</TimeStamp>
    <Nonce><![CDATA[${nonce}$]]></Nonce>
</xml>
```

8）Encrypt 的生成方法：
  - AESKey = Base64_Decode(EncodingAESKey + "=")，EncodingAESKey 尾部填充一个字符的 "="，用 Base64_Decode 生成 32 个字节的 AESKey
  - 构造 FullStr = random(16B) + msg_len(4B) + msg + appid，其中：
    - random(16B) 为 16 字节的随机字符串
    - msg_len 为 msg 长度，占 4 个字节(网络字节序)
    - msg 为明文
    - appid 为公众号 Appid
  - 在此示例中：
    - random(16B) = "707722b803182950"
    - msg_len = 25（注意：需按网络字节序，占 4 个字节）
    - msg = `{"demo_resp":"good luck"}`
    - appid = "wxba5fad812f8e6fb9"
    - FullStr 的字节大小为 63
  - 将 FullStr 用 AESKey 进行加密，得到 TmpMsg，字节大小为 64。AES 采用 CBC 模式，秘钥长度为 32 个字节（256 位），数据采用 PKCS#7 填充。微信团队提供了多种语言的示例代码（包括 PHP、Java、C++、Python、C#），请开发者尽量使用示例代码。[示例下载](https://wximg.gtimg.com/shake_tv/mpwiki/cryptoDemo.zip)
  - 对 TmpMsg 进行 Base64 编码，得到 Encrypt = `"ELGduP2YcVatjqIS+eZbp80MNLoAUWvzzyJxgGzxZO/5sAvd070Bs6qrLARC9nVHm48Y4hyRbtzve1L32tmxSQ=="`

9）TimeStamp 由开发者生成，使用当前时间戳即可，示例使用 1713424427。

10）Nonce 回填 URL 参数中的 nonce 参数即可，示例使用 415670741。

11）MsgSignature 的生成方法：
  - 将 token、TimeStamp（回包中的）、Nonce（回包中的）、Encrypt（回包中的）四个参数进行字典序排序
  - 将四个参数字符串拼接成一个字符串，并进行 sha1 计算签名：`1b9339964ed2e271e7c7b6ff2b0ef902fc94dea1`

12）最终回包为：

```json
{
    "Encrypt": "ELGduP2YcVatjqIS+eZbp80MNLoAUWvzzyJxgGzxZO/5sAvd070Bs6qrLARC9nVHm48Y4hyRbtzve1L32tmxSQ==",
    "MsgSignature": "1b9339964ed2e271e7c7b6ff2b0ef902fc94dea1",
    "TimeStamp": 1713424427,
    "Nonce": "415670741"
}
```

为了便于开发者调试，平台提供了相关的调试工具（[请求构造](https://developers.weixin.qq.com/apiExplorer?type=messagePush)、[调试工具](https://developers.weixin.qq.com/apiExplorer?type=messagePush)）供开发者使用。

- 「请求构造」允许开发者填写相关参数后，生成 debug_demo 事件发包或回包的相关调试信息，供开发者使用。
- 「调试工具」允许开发者填写 [access_token](/doc/service/guide/dev/api/README.md)、Body 后，微信服务器会拉取你在公众号后台配置的消息推送配置，实际推送一条 debug_demo 事件供开发者调试。
