's'
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -43,24 +43,36 @@ def aes_decrypt_base64(cipher_b64: str, encoding_aes_key: str) -> str:
|
|||||||
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
|
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
|
||||||
decrypted = cipher.decrypt(cipher_bytes)
|
decrypted = cipher.decrypt(cipher_bytes)
|
||||||
decrypted = pkcs5_unpadding(decrypted)
|
decrypted = pkcs5_unpadding(decrypted)
|
||||||
# 微信消息体格式:16B随机串 + 4B网络序长度 + 明文 + appid
|
# 兼容两种格式:
|
||||||
|
# 1) 微信消息体:16B随机串 + 4B长度 + 明文 + appid
|
||||||
|
# 2) 纯文本:直接是明文 JSON/XML
|
||||||
|
try:
|
||||||
if len(decrypted) < 20:
|
if len(decrypted) < 20:
|
||||||
raise ValueError("解密后消息长度非法")
|
raise ValueError("payload too short")
|
||||||
content = decrypted[16:]
|
content = decrypted[16:]
|
||||||
msg_len = struct.unpack("!I", content[:4])[0]
|
msg_len = struct.unpack("!I", content[:4])[0]
|
||||||
msg_start = 4
|
msg_start = 4
|
||||||
msg_end = msg_start + msg_len
|
msg_end = msg_start + msg_len
|
||||||
if msg_end > len(content):
|
if msg_end > len(content):
|
||||||
raise ValueError("解密后消息长度字段越界")
|
raise ValueError("msg length out of range")
|
||||||
msg = content[msg_start:msg_end]
|
msg = content[msg_start:msg_end]
|
||||||
return msg.decode("utf-8")
|
text = msg.decode("utf-8")
|
||||||
|
if text:
|
||||||
|
return text
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return decrypted.decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
def aes_encrypt_base64(plaintext: str, encoding_aes_key: str, app_id: str) -> str:
|
def aes_encrypt_base64(plaintext: str, encoding_aes_key: str, app_id: str | None = None) -> str:
|
||||||
aes_key, iv = get_aes_key_and_iv(encoding_aes_key)
|
aes_key, iv = get_aes_key_and_iv(encoding_aes_key)
|
||||||
raw = plaintext.encode("utf-8")
|
raw = plaintext.encode("utf-8")
|
||||||
|
if app_id:
|
||||||
# 微信消息体格式:16B随机串 + 4B网络序长度 + 明文 + appid
|
# 微信消息体格式:16B随机串 + 4B网络序长度 + 明文 + appid
|
||||||
data = os.urandom(16) + struct.pack("!I", len(raw)) + raw + app_id.encode("utf-8")
|
data = os.urandom(16) + struct.pack("!I", len(raw)) + raw + app_id.encode("utf-8")
|
||||||
|
else:
|
||||||
|
# 纯明文格式
|
||||||
|
data = raw
|
||||||
padded = pkcs5_padding(data)
|
padded = pkcs5_padding(data)
|
||||||
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
|
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
|
||||||
encrypted = cipher.encrypt(padded)
|
encrypted = cipher.encrypt(padded)
|
||||||
|
|||||||
9
main.py
9
main.py
@@ -597,10 +597,15 @@ async def _handle_wechat_request(request: Request, app_id: Optional[str], backgr
|
|||||||
logger.info("回包明文(加密前): %s", resp_json_str)
|
logger.info("回包明文(加密前): %s", resp_json_str)
|
||||||
|
|
||||||
# 8. 加密响应
|
# 8. 加密响应
|
||||||
# XML 回调场景使用当前会话 appid(公众号 appid)加密,避免与开放 API appid 不一致导致平台解密失败。
|
# - /?app_id=... 的第三方服务接口请求通常使用纯AES明文加密(不拼接 appid)
|
||||||
response_app_id = APP_ID
|
# - XML 回调场景使用会话 appid 封装加密
|
||||||
|
# - 其余 JSON 场景默认使用纯AES,提升兼容性
|
||||||
|
response_app_id: Optional[str] = None
|
||||||
if str(data.get("_payload_format", "")) == "xml":
|
if str(data.get("_payload_format", "")) == "xml":
|
||||||
response_app_id = str(data.get("ChannelId", "") or data.get("appid", "")).strip() or APP_ID
|
response_app_id = str(data.get("ChannelId", "") or data.get("appid", "")).strip() or APP_ID
|
||||||
|
elif app_id:
|
||||||
|
response_app_id = None
|
||||||
|
logger.info("响应加密模式: %s", "envelope" if response_app_id else "plain")
|
||||||
try:
|
try:
|
||||||
encrypted_resp = aes_encrypt_base64(resp_json_str, ENCODING_AES_KEY, response_app_id)
|
encrypted_resp = aes_encrypt_base64(resp_json_str, ENCODING_AES_KEY, response_app_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user