diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc index 272594c..f416cb9 100644 Binary files a/__pycache__/config.cpython-312.pyc and b/__pycache__/config.cpython-312.pyc differ diff --git a/__pycache__/crypto_utils.cpython-312.pyc b/__pycache__/crypto_utils.cpython-312.pyc index aa20361..a9b545f 100644 Binary files a/__pycache__/crypto_utils.cpython-312.pyc and b/__pycache__/crypto_utils.cpython-312.pyc differ diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc index 9f5730c..83a2cdb 100644 Binary files a/__pycache__/main.cpython-312.pyc and b/__pycache__/main.cpython-312.pyc differ diff --git a/crypto_utils.py b/crypto_utils.py index 096b69c..025568c 100644 --- a/crypto_utils.py +++ b/crypto_utils.py @@ -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) decrypted = cipher.decrypt(cipher_bytes) decrypted = pkcs5_unpadding(decrypted) - # 微信消息体格式:16B随机串 + 4B网络序长度 + 明文 + appid - if len(decrypted) < 20: - raise ValueError("解密后消息长度非法") - content = decrypted[16:] - msg_len = struct.unpack("!I", content[:4])[0] - msg_start = 4 - msg_end = msg_start + msg_len - if msg_end > len(content): - raise ValueError("解密后消息长度字段越界") - msg = content[msg_start:msg_end] - return msg.decode("utf-8") + # 兼容两种格式: + # 1) 微信消息体:16B随机串 + 4B长度 + 明文 + appid + # 2) 纯文本:直接是明文 JSON/XML + try: + if len(decrypted) < 20: + raise ValueError("payload too short") + content = decrypted[16:] + msg_len = struct.unpack("!I", content[:4])[0] + msg_start = 4 + msg_end = msg_start + msg_len + if msg_end > len(content): + raise ValueError("msg length out of range") + msg = content[msg_start:msg_end] + 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) raw = plaintext.encode("utf-8") - # 微信消息体格式:16B随机串 + 4B网络序长度 + 明文 + appid - data = os.urandom(16) + struct.pack("!I", len(raw)) + raw + app_id.encode("utf-8") + if app_id: + # 微信消息体格式:16B随机串 + 4B网络序长度 + 明文 + appid + data = os.urandom(16) + struct.pack("!I", len(raw)) + raw + app_id.encode("utf-8") + else: + # 纯明文格式 + data = raw padded = pkcs5_padding(data) cipher = AES.new(aes_key, AES.MODE_CBC, iv) encrypted = cipher.encrypt(padded) diff --git a/main.py b/main.py index c8f1321..8d11934 100644 --- a/main.py +++ b/main.py @@ -597,10 +597,15 @@ async def _handle_wechat_request(request: Request, app_id: Optional[str], backgr logger.info("回包明文(加密前): %s", resp_json_str) # 8. 加密响应 - # XML 回调场景使用当前会话 appid(公众号 appid)加密,避免与开放 API appid 不一致导致平台解密失败。 - response_app_id = APP_ID + # - /?app_id=... 的第三方服务接口请求通常使用纯AES明文加密(不拼接 appid) + # - XML 回调场景使用会话 appid 封装加密 + # - 其余 JSON 场景默认使用纯AES,提升兼容性 + response_app_id: Optional[str] = None if str(data.get("_payload_format", "")) == "xml": 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: encrypted_resp = aes_encrypt_base64(resp_json_str, ENCODING_AES_KEY, response_app_id) except Exception as e: