数据包加解密
<h2>1.简介</h2>
<p>当事件(如聊天消息、成员变更等)发生或下发任务(如获取群二维码等)执行完成时,会推送数据到用户开发的回调地址,用户可根据接收到的数据进行相应的逻辑处理。推送的数据已加密,下面介绍加解密相关内容。解密后的数据结构和类型请参考:<a href="<a href="https://www.showdoc.com.cn/yunzhenji/11490693160849765">被动接收数据</a>">https://www.showdoc.com.cn/yunzhenji/11490693160849765">被动接收数据</a></a>;</p>
<p><font color="red">注意: 回调请求<strong>超时时间</strong>为<strong>1s</strong>,故在开发回调地址时,接收到数据后请先存入消息队列,复杂的业务逻辑之后慢慢消费队列,以免造成业务执行不完的问题。 另需注意,为了保证回调整体性能,避免因某些回调地址异常且有大批量回调占用耗时的情况,目前有自动触发限制的机制:当天回调超时(<strong>超时时间</strong>为<strong>1s</strong>)超过1000次,会被自动加入限制,当天后续不会再回调。API用户遇到不回调的情况,可以先查验回调地址是否异常,排除异常后可以联系我们移出限制。</font></p>
<h2>2. 数据加解密</h2>
<p> 为确保数据推送的安全性,推送到用户回调地址的数据已被整体加密,用户需要对接收到的数据进行对称解密后使用。
加密采用AES算法的CBC模式,秘钥长度为32个字节(在申请API应用时已返回,即:encoding_aes_key),算法初始向量使用秘钥(即:encoding_aes_key)的前16个字节,数据采用PKCS#7方式填充。
加密前的数据为json字符串,包含多条消息通知,数据结构如:"[{"type":"chatMsg ","data":{}},{"type":"contacts ","data":{}}]",作为待加密数据,对该数据采用PKCS#7进行填充后,使用AES-256-CBC算法进行对称加密。</p>
<p><em><font color="red">注:1、PKCS#7:K为秘钥字节数(采用32),buf为待加密的内容,N为其字节数。Buf需要被填充为K的整数倍。在buf的尾部填充(K-N%K)个字节,每个字节的内容是(K- N%K)对应的ASCII 字符;
2、填充时明文分组长度为256位(32字节),与微信开放平台AES-256-CBC加密填充方式一致,与AES加密标准固定块长128位(16字节)不同,所以加密时注意先按32字节填充,调用加密方法时采用不填充模式;</font></em></p>
<p>php基于openssl加解密示例:</p>
<pre><code>/**
* 填充算法
* @param string $source
* @return string
*/
function addPKCS7Padding($source, $blocksize = 16) {
$datasize = strlen($source);
$padsize = $blocksize - ($datasize % $blocksize);
$char = chr($padsize);
$source .= str_repeat($char, $padsize);
return $source;
}
/**
* 移去填充算法
* @param string $source
* @return string
*/
function stripPKSC7Padding($source, $blocksize = 16) {
$char = substr($source, -1, 1);
$num = ord($char);
if ($num &gt; $blocksize) {
return $source;
}
return substr($source, 0, -$num);
}
加密示例:
$data = &quot;123456&quot;;
$encoding_aes_key = &quot;4b7ee5e6210e056fb00ff518d1653854&quot;;
$data = addPKCS7Padding($data, 32);//填充至32字节的倍数
$iv = substr($encoding_aes_key, 0, 16);
$encryptedData = openssl_encrypt($data, 'AES-256-CBC', $encoding_aes_key, OPENSSL_NO_PADDING, $iv);
$encryptedData = base64_encode($encryptedData);
print_r($encryptedData);//打印结果:&quot;slinTeomuAR91ljVsl0qSZZLtpfGpJ/gDP8nRur1GA8=&quot;
解密示例:
$encryptedData = base64_decode($encryptedData);
$data = openssl_decrypt($encryptedData, 'AES-256-CBC', $encoding_aes_key, OPENSSL_NO_PADDING, $iv);
$data = stripPKSC7Padding($data, 32);//移去填充字符
print_r($data);//打印结果:&quot;123456&quot;</code></pre>
<p><font color="red"><strong>java加解密可参考:</strong></p>
<pre><code>import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
* Unit test for simple App.
*/
public class Demo
{
public static void main(String[] args) {
try {
String rawContent = &quot;123456&quot;;
String aesKey = &quot;4b7ee5e6210e056fb00ff518d1653854&quot;;
String iv = aesKey.substring(0,16);
String enResult = encrypt(rawContent, aesKey, iv);
System.out.println(enResult);
String deResult = decrypt(enResult, aesKey, iv);
System.out.println(deResult);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 加密
* @param data
* @param key
* @param iv
* @return
* @throws Exception
*/
public static String encrypt(String data, String key, String iv) throws Exception {
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), &quot;AES&quot;);
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
Cipher cipher = Cipher.getInstance(&quot;AES/CBC/NoPadding&quot;);//&quot;算法/模式/补码方式&quot;
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(PKCS7Encode(data).getBytes());
return new Base64().encodeToString(encrypted);
}
/**
* 解密
* @param encryptData
* @param key
* @param iv
* @return
* @throws Exception
*/
public static String decrypt(String encryptData, String key, String iv) throws Exception {
byte[] encrypted1 = new Base64().decode(encryptData);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), &quot;AES&quot;);
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
Cipher cipher = Cipher.getInstance(&quot;AES/CBC/NoPadding&quot;);
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
// 去除补位字符
byte[] bytes = PKCS7Decode(original);
return new String(bytes);
}
/**
* 补全
* @param data
* @return
*/
public static String PKCS7Encode(String data) {
int blocksize = 32;
int padsize = blocksize - (data.getBytes().length % blocksize);
if (padsize &lt;= 0) {
return data;
}
char[] result = new char[padsize];
for (int i = 0; i &lt; padsize; i++) {
result[i] = (char) padsize;
}
data += new String(result);
return data;
}
/**
* 剔除补全
* @param decrypted
* @return
*/
static byte[] PKCS7Decode(byte[] decrypted) {
int pad = (int)decrypted[decrypted.length - 1];
if (pad &lt; 1 || pad &gt; 32) {
pad = 0;
}
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}
}
</code></pre>
<p><strong>其他语言加解密参考微信公众号同类型加解密demo:</strong></font>
<a href="https://cdn-qyb-hz.wxb.com/u/21/20220419/f5e59ac09ddbc732f2653ed59d6eb880.zip">https://cdn-qyb-hz.wxb.com/u/21/20220419/f5e59ac09ddbc732f2653ed59d6eb880.zip</a></p>
<h1>3.解密后的数据结构和类型,点击跳至:<a href="<a href="https://www.showdoc.com.cn/1702411877508139/7988199114453636">解密后的数据结构介绍</a>查看">https://www.showdoc.com.cn/1702411877508139/7988199114453636">解密后的数据结构介绍</a>查看</a>。</h1>