* 注意事项:
- 公司/集团运维放开 企微回调的 IP 地址的白名单:【获取企业微信回调 IP 段】:https://developer.work.weixin.qq.com/document/path/92521
- 主体与域名备案的主体一致:https://open.work.weixin.qq.com/wwopen/common/readDocument/40754
* 配置步骤:
1.配置 webhook,参数【body 获取全文】 拿到对应的参数值,/PBP(封装业务流)请开启 API
- 1.1 webhook 参数配置
- 1.2 PBP 参数配置
- 流程配置:
- 入参配置以及参数释义:
- 地址验证解析以及密文解析:
在代码逻辑中:
1.添加签名校验 防止信息篡改
2.添加地址验证 还是应用消息信息判断,按实际解密后进行数据转换,消息也提业务(依据:验证地址发送 echostr ,不发送 msg_encrypt;地址证通过后才发 msg_encrypt 的 对应的 echostr 不发送。 )效果如下
- 配置输出参数
webhook
PBP
整个流程到此配置结束。
python 解密整合
import base64
import string
import random
import hashlib
import time
import struct
from Crypto.Cipher import AES
import socket
import xmltodict
import json
key = base64.b64decode(input["key"]+"=")
mode = AES.MODE_CBC
block_size = 32
def getSHA1(token, timestamp, nonce, msg):
sortlist = [token, timestamp, nonce, msg]
sortlist.sort()
sha = hashlib.sha1()
sha.update("".join(sortlist).encode())
return sha.hexdigest()
def PKCS7Encoder_encode(text):
text_length = len(text)
# 计算需要填充的位数
amount_to_pad = block_size - (text_length % block_size)
if amount_to_pad == 0:
amount_to_pad = block_size
# 获得补位所用的字符
pad = chr(amount_to_pad)
return text + (pad * amount_to_pad).encode()
def PKCS7Encoder_decode(decrypted):
pad = decrypted[-1]
if pad<1 or pad >32:
pad = 0
return decrypted[:-pad]
def encrypt(text,receiveid):
# 16位随机字符串添加到明文开头
text = text.encode()
text = get_random_str() + struct.pack("I", socket.htonl(len(text))) + text + receiveid.encode()
# 使用自定义的填充方式对明文进行补位填充
text = PKCS7Encoder_encode(text)
# 加密
cryptor = AES.new(key,mode,key[:16])
try:
ciphertext = cryptor.encrypt(text)
# 使用BASE64对加密后的字符串进行编码
return base64.b64encode(ciphertext)
except Exception as e:
return str(e)
def decrypt(text):
try:
cryptor = AES.new(key,mode,key[:16])
# 使用BASE64对密文进行解码,然后AES-CBC解密
plain_text = cryptor.decrypt(base64.b64decode(text))
except Exception as e:
return {"msg": "BASE64对密文进行解码:"+str(e,) ,"content":""}
#try:
pad =plain_text[-1]
# 去除16位随机字符串
content = plain_text[16:-pad]
xml_len = socket.ntohl(struct.unpack("I",content[ : 4])[0])
xml_content = content[4 : xml_len+4]
from_receiveid = content[xml_len+4:]
return {"msg":'解析成功',"content":str(xml_content,'utf-8')}
#json.dumps(xmltodict.parse())
def get_random_str():
return str(random.randint(1000000000000000, 9999999999999999)).encode()
nsignature=getSHA1(input["token"],input["timestamp"],input["nonce"],input["msg_encrypt"])
content=input['echostr']
ismessage=False
if(len(content)==0):
ismessage=True
content=input['msg_encrypt']
result=decrypt(content)
if ismessage:
result['content']=json.dumps(xmltodict.parse(result['content'],'utf-8'))
output={"签名":nsignature,"isTrue":nsignature==input['signature'],
"密文解密":result
}
nodejs 解密:
///企微消息解密
const crypto = require('crypto')
const EncodingAESKey = input.EncodingAESKey;
const aesMsg = input.echostr
function _decode(data, aesKeyEncoding) {
let aesKey = Buffer.from(aesKeyEncoding + '=', 'base64');
let aesCipher = crypto.createDecipheriv("aes-256-cbc", aesKey, aesKey.slice(0, 16));
aesCipher.setAutoPadding(false);
let decipheredBuff = Buffer.concat([aesCipher.update(data, 'base64'), aesCipher.final()]);
decipheredBuff = PKCS7Decoder(decipheredBuff);
let len_netOrder_corpid = decipheredBuff.slice(16);
let msg_len = len_netOrder_corpid.slice(0, 4).readUInt32BE(0);
const result = len_netOrder_corpid.slice(4, msg_len + 4).toString();
return result; // 返回一个解密后的明文-
}
function PKCS7Decoder (buff) {
var pad = buff[buff.length - 1];
if (pad < 1 || pad > 32) {
pad = 0;
}
return buff.slice(0, buff.length - pad);
}
return { decryptMsg: _decode(aesMsg, EncodingAESKey) }
//// xml转json
const Xml2js = require('xml2js');
var xml={};
const Parser = new Xml2js.Parser({explicitArray: false, ignoreAttrs: false});
Parser.parseString(input.xml, function (err, data) {
if (err) console.log(err);
xml=data
});
output=xml