From 80a4b2d0f9510934b65e6248ed19ffdd920ff4f8 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 31 Mar 2026 11:27:39 -0500 Subject: [PATCH] 's' --- __pycache__/config.cpython-312.pyc | Bin 1661 -> 1680 bytes __pycache__/crypto_utils.cpython-312.pyc | Bin 4386 -> 4568 bytes __pycache__/main.cpython-312.pyc | Bin 48551 -> 48677 bytes crypto_utils.py | 40 +++++++++++++++-------- main.py | 9 +++-- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/__pycache__/config.cpython-312.pyc b/__pycache__/config.cpython-312.pyc index 272594c1167aa93e301cdfb874df194f1fd76fa7..f416cb9e469dfe5dc4b55ff0fc3a98313cd7a499 100644 GIT binary patch delta 45 zcmey%Gl7@uG%qg~0}$-~vXRS^RYb7%Q6y9I&+Pn5T>m*KboNnT{?g|_=ZQ2wXN|U-pAx%hsgqo^STgPi= z*NS5qa^c{rAQDXxzF{gPQVyud9CA!i5lBVIu0c4f2yx)T1(_BQ%840gr1!mVzV~Ld z^WNM0L#>eKiQDZW(8~YwB>j=N>FFbX+>C4ldo{N%YEEPxWMq*e2+g63%e*GN$8P=* zCjDPM%aG&)xJb>By|pi?-^gwsy2f_B70=}L3ji2ErGX#bXO5E!{Fy0|h1xv(CP_I7 z_=)3J&pYmt8&DIJn3u>mkBY@TcH4OaPiB7Kr!zSBV0>K&+CgFlQ6s5Q_c5 zVCY6r?8?R0GDcieEW=RDlmRU80@C-X!D&F@1pkVeR5E%tX{8h+Zz;yI0^-@EzR%{Z z<>+a(3n(bfW^NhjLs^?PEnu@|E|azZehmm<=!ZW32pNR0@PPr_d7+Tdb5`2OsvaUya%*Yp?dt8OeEi{PLq1vNTHQTmCr(W^ zXCqzZ>Yk%zx+TdU7Vi`*k;_eKsLbpNQp@Kr z&$MYm^tO3I?D<6qG=xCq^`3)f@Iyhyx69Z-#2kdzKKkph((FVQD^LB z`tfNJA>>^uwMD|qY!J@7!c2(*GcfPE$7HDz3pt^_9zh2?jR8jxQ>BBVJM?VA&~%$J zOqAH@v^FJgEJhdFc*tmUjudu->@sj-8bz%dk6UEo!m z*=fsJ%m(@~9!6l(AifG5m^pHBP17|n27x&4nnu!jWabyJCJw*0%ejA-x;ph5y8G^D zj?aZpg{|1vp@)gu(WY{yA)VPyG^EL$i{H&Ynyp`1XvS_dq#JjbwuA8c%1ljgQ`7{0 zw943ADsHAS=_Q+9(N2PMNVJ_Py`ZI&x@oBn@CH)A42DY>W--jcX(_^9L-Y#FOPBgU z7ow_sFpl#$V-mv%{7DLr$e7~40pO7Xor0;DDH!nZ0sK6hPG>D}4Mw~{*LAcfk(muR z=he>z$Z+*Un?Pd^Yl#pgKE9)N#($NmR@ Cd@e!& delta 967 zcmZ`%OH30{6lJEe6kic5H*S@q9G!2BW@HwF|BpcOw_m#l(-==ZrJF%!`eyix%b_B?wfbddr9Z5rQ$n5 z;8Muf{Og_1!}p3=)T>@sBtuXZ$rfTFF;-c0&@88(WqLmqQJGKX+bBxHE%a_GV{DxM zMy02bc8zQDw1t%I2nCR2K$&=$DWeMT7v?0ripFm_$JkM0DTVK|Upx1tNc?Us1!a-8 zSq31TKq!~M&wID0N6$}3uFQ;g&72;c?SC=-{O;_Hn=@nmD4W#h>B2fh1H15l?CFuK zGmoDB9KZ0R|I)lM6g*h6L9(I(Lf4o>0iR!6Y*2$pGpZeJz9535qa1)-++Z$VlLtJO z5JKLd#)kYI9`9j|l@)J5RwMx}0cIwE10Wxdm=73LfyCHmad8zSC?ckEc#Gw7=XVp= zRdME2l>TVS`7CC|vezd>cg*Zgii@Lk)X?P**M1h$d)m9(quT~uLraHB2UjM<)k8<( zVrjTGX|;vxfa;2ig=A?G`|OeZIPas$p>MV+Ay&oARY}ekk)pLRZdrJ1k{5d_yDK9t z2|ho@u=ywzPBy?v)J^<`+lsAxA0FmEbQY7et|x-iN~sI-u!Z)yo$N?gwd;*#Q zd5$3#BDTXL%ZiN3uw8I-H6#UGbpY=bmX&|!1%0mOi`Ca_2I{ZX4>1qDkDU)%6M619 z@1AhR`Kn3wweV7yYDsKvit|n3ZDY>_n%)GHN{nV|^{B0FzGltPF0VsX@IyRAU>?sNH`nxD)E}-0F+~;bs)^m^?<;H#OXR!R926YRD0cE z%l0y~0YZTAZR;K|;kVXOn8Cdb(W1Iu0`l*jsEf6rPRK_$;QD8r-*6Z2kr)+T=_O}FE}++l(gHky3OuBXLq%= z+gJH^*GZ>a>^rpxVTGjW4{~~%M2l^a;nyQjavDx^980(+|0tDzh)wL5f8xTfR$i*~ zD^h&&M`>@4zvJp~w<8g(_?F`fqX*pGRB%xTv(ZqM!p+8%D$P20l~$^*N=I`tg;9a# z62pxpXf9QsPMp_K`h@Ci^fourq(<`qCKWf&aFiuwk;Gq$Qktb`DVe35o@kV@%TmhA z5kOfMVnv3rm|ZRoaY%AVkob~0p<7*tAIRVMV(}pumOfD0Ujtz zLM)YCHzLelu9Bk1- z36{AIfdN;!;`uV7_KIz;IN-|(UKWR1NA^n$bejREj_u+r2o8$ov9A(1iu1kGyom?} z;xBKM6mqb9vP5%&F779@6b9&}hLo!j9;d_I*x_uoH@12N3qCWc4{sty8%L)181SZ7 zD+VV=A-ax;shmK6G~ozs6rxIT_}MgM(s>K?Wds^tI87JPzyggb&}0HlBhbzyZjUbp q&WoeIJ4{A5KZMtUn{ zP+ZZwUinbun>ZHsAY=)V+T45@L(5GI+x31}pL0tzfpC0GK+ zR2(J5J`@Kxx(#!0NVLBb_J|U+kd1yDRFh9Zv#1Z%5P853wX|a3jzqs)*@Rp-AhgeV z>;ioe+7jf$Y-A}RbE6l9StI#AS_&#Lk9~$F@?)&&x((JW;5A5VN0dUp4PHAHo3{Y1 z8w$J)XuVYLD@W@Uo=TQ^Yxb0M7om6c`Dzy008k}qLy$P@I_g>eeRH8&g;IKvajKxy zTPLQfG_D2Cbs9Igd{%MW%6YrIy4xi7q)mL1E^??KrrS7Y5HQYw#uBg-GFivfN-^Wm z6en=5)VM0gUnXYUc7L(BQ!F#yDPuZ?@iG86u})OWt?WOHkV_N0x@JbWg|tR&ay!E* zG8Az@8@U-d1q#`U9FsjvP0+8AqadGVI71DScN=vIU#5_y`EvOT!&#c1-_yZ)l6<@> z`4&^ncyw}bIvgDyjK=U~vY+(mM_6KVq2mDYai!^1$FsJ7oDNR>hpE^%EqgFYKfdgO Ezb$XbhyVZp 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: