wx_pokemon_aes_tool.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. # -*- coding: utf-8 -*-
  2. # Author : Charley
  3. # Python : 3.10.8
  4. # Date : 2025/8/26 14:31
  5. from Crypto.Cipher import AES
  6. from Crypto.Util.Padding import pad, unpad
  7. import json
  8. import base64
  9. import hashlib
  10. import time
  11. import random
  12. import execjs
  13. from loguru import logger
  14. # def _define_property(obj, key, value):
  15. # """
  16. # 模拟JavaScript中的_defineProperty函数
  17. # 相当于 Object.defineProperty(obj, key, {value: value, enumerable: true, configurable: true, writable: true})
  18. # 简单来说就是给对象添加一个可枚举的属性
  19. # """
  20. # # 在Python中,这相当于直接给字典添加键值对
  21. # result = {}
  22. # if isinstance(obj, dict):
  23. # result.update(obj)
  24. # result[key] = value
  25. # return result
  26. #
  27. #
  28. # def process_encrypted_params(params, method="POST", encryption_enable=True):
  29. # """
  30. # 处理加密参数,模拟JavaScript中的逻辑
  31. #
  32. # P = s.default.aesEncrypt(A),
  33. # E = this.encryptionEnable ? "{}" == A ? {} : n({}, "GET" == o ? "encryptionUrlParams" : "encryptionBodyParams", "GET" == o ? encodeURIComponent(P) : P) : i,
  34. # """
  35. # if not encryption_enable:
  36. # return params
  37. #
  38. # # 将参数转换为JSON字符串 (A)
  39. # if isinstance(params, dict) and not params: # 空字典
  40. # A = "{}"
  41. # elif params is None:
  42. # A = "{}"
  43. # else:
  44. # A = json.dumps(params, separators=(',', ':'), ensure_ascii=False)
  45. #
  46. # # 如果是空对象,返回空字典
  47. # if A == "{}":
  48. # return {}
  49. #
  50. # # 进行AES加密得到 P
  51. # P = pokemon_aes_encrypt(A)
  52. #
  53. # # 根据HTTP方法确定参数名和是否URL编码
  54. # if method.upper() == "GET":
  55. # param_name = "encryptionUrlParams"
  56. # param_value = quote(P, safe='') if P else P
  57. #
  58. # else:
  59. # param_name = "encryptionBodyParams"
  60. # param_value = P
  61. #
  62. # # 使用_define_property创建最终对象 E
  63. # E = _define_property({}, param_name, param_value)
  64. #
  65. # return E
  66. class PokemonAESUtil:
  67. """宝可梦卡牌数据AES加解密工具类"""
  68. def __init__(self):
  69. # 固定的密钥字符串
  70. KEY_STRING = "Njda*7^%1<.)0=+u&%hkfs;k"
  71. # 将密钥字符串转换为字节
  72. self.key_bytes = KEY_STRING.encode('utf-8')
  73. # CryptoJS 在 keySize 未明确指定或不标准时,会使用密钥字节的前 16/24/32 字节
  74. # 由于密钥是 24 字节,使用 AES-192 (24 字节密钥)
  75. if len(self.key_bytes) < 24:
  76. # 用 null 字节填充到 24 字节
  77. self.key_bytes = self.key_bytes.ljust(24, b'\0')
  78. elif len(self.key_bytes) > 24:
  79. # 截取前 24 字节
  80. self.key_bytes = self.key_bytes[:24]
  81. def encrypt(self, data):
  82. """
  83. AES ECB 模式加密,PKCS7 填充
  84. :param data: 要加密的数据 (字典或字符串)
  85. :return: Base64 编码的密文字符串
  86. """
  87. # 创建 AES ECB 模式加密器
  88. cipher = AES.new(self.key_bytes, AES.MODE_ECB)
  89. # 处理输入数据
  90. if isinstance(data, dict):
  91. # 如果是字典,转换为紧凑格式的JSON字符串
  92. plaintext = json.dumps(data, separators=(',', ':'))
  93. else:
  94. # 如果是字符串,直接使用
  95. plaintext = str(data)
  96. # 将明文字符串编码为 UTF-8 字节
  97. plaintext_bytes = plaintext.encode('utf-8')
  98. # 使用 PKCS7 填充
  99. padded_plaintext = pad(plaintext_bytes, AES.block_size)
  100. # 执行加密
  101. ciphertext_bytes = cipher.encrypt(padded_plaintext)
  102. # 将密文字节进行 Base64 编码并转换为字符串
  103. ciphertext_b64 = base64.b64encode(ciphertext_bytes).decode('utf-8')
  104. return ciphertext_b64
  105. def decrypt(self, ciphertext_b64):
  106. """
  107. AES ECB 模式解密,PKCS7 去填充
  108. :param ciphertext_b64: Base64 编码的密文字符串
  109. :return: 解密后的原始对象 (通常是字典)
  110. """
  111. # 创建 AES ECB 模式解密器
  112. cipher = AES.new(self.key_bytes, AES.MODE_ECB)
  113. # 将 Base64 字符串解码为字节
  114. ciphertext_bytes = base64.b64decode(ciphertext_b64)
  115. # 执行解密
  116. padded_plaintext_bytes = cipher.decrypt(ciphertext_bytes)
  117. # 移除 PKCS7 填充
  118. plaintext_bytes = unpad(padded_plaintext_bytes, AES.block_size)
  119. # 将字节解码为 UTF-8 字符串
  120. plaintext = plaintext_bytes.decode('utf-8')
  121. # 尝试解析 JSON 字符串为 Python 对象,如果失败则返回原始字符串
  122. try:
  123. return json.loads(plaintext)
  124. except json.JSONDecodeError:
  125. return plaintext
  126. # 创建全局实例,方便直接调用
  127. aes_util = PokemonAESUtil()
  128. # 便捷函数,可以直接调用
  129. def pokemon_aes_encrypt(data):
  130. """
  131. 便捷加密函数
  132. :param data: 要加密的数据 (字典或字符串)
  133. :return: Base64 编码的密文字符串
  134. """
  135. return aes_util.encrypt(data)
  136. def pokemon_aes_decrypt(ciphertext_b64):
  137. """
  138. 便捷解密函数
  139. :param ciphertext_b64: Base64 编码的密文字符串
  140. :return: 解密后的原始对象
  141. """
  142. return aes_util.decrypt(ciphertext_b64)
  143. """
  144. function n(e, r, t) {
  145. var n = new Array
  146. , a = 0;
  147. for (var i in e)
  148. n[a] = i,
  149. a++;
  150. var o = n.sort()
  151. , u = {};
  152. for (var s in o)
  153. u[o[s]] = e[o[s]];
  154. if (Array.isArray(e)) {
  155. if (r)
  156. return JSON.parse(JSON.stringify(e));
  157. u = {
  158. str: JSON.stringify(e)
  159. }
  160. }
  161. return JSON.parse(JSON.stringify(u, (function(e, r) {
  162. if (e) {
  163. if (null == r || null == r)
  164. return;
  165. return r instanceof Object ? t ? r : JSON.stringify(r) : "" + r
  166. }
  167. return r
  168. }
  169. )))
  170. }
  171. """
  172. def n(params, is_secret, encryption_enable):
  173. """
  174. 模拟JavaScript中的n函数
  175. 参数说明:
  176. params: 要处理的参数对象 (对应JavaScript中的e)
  177. is_secret: 是否为secret处理 (对应JavaScript中的r)
  178. encryption_enable: 是否启用加密 (对应JavaScript中的t)
  179. JavaScript源码分析:
  180. 1. 对对象的键进行排序
  181. 2. 如果是数组,根据is_secret决定处理方式
  182. 3. 使用自定义序列化函数处理所有值
  183. """
  184. # 如果params是None,直接返回
  185. if params is None:
  186. return params
  187. # 初始化变量(模拟JavaScript变量声明)
  188. keys_array = []
  189. index = 0
  190. # 模拟 for (var i in e) 循环,提取所有键
  191. # 注意:对于数组,键是索引(0, 1, 2, ...)
  192. if isinstance(params, (dict, list)):
  193. if isinstance(params, dict):
  194. # 对于字典,提取键
  195. for key in params:
  196. keys_array.append(key)
  197. index += 1
  198. else:
  199. # 对于数组,提取索引作为键
  200. for i in range(len(params)):
  201. keys_array.append(str(i))
  202. index += 1
  203. # 排序键
  204. sorted_keys = sorted(keys_array)
  205. # 创建新对象 u,按键的排序顺序重建
  206. u = {}
  207. if isinstance(params, dict):
  208. for key in sorted_keys:
  209. u[key] = params[key]
  210. elif isinstance(params, list):
  211. for key in sorted_keys:
  212. # key是字符串索引,需要转为整数
  213. idx = int(key)
  214. u[key] = params[idx]
  215. # 检查是否为数组
  216. if isinstance(params, list):
  217. if is_secret:
  218. return json.loads(json.dumps(params, separators=(',', ':')))
  219. else:
  220. u = {
  221. "str": json.dumps(params, separators=(',', ':'))
  222. }
  223. # 自定义序列化函数(模拟JavaScript的replacer函数)
  224. def replacer(key_, value):
  225. # key为空字符串表示是根对象
  226. if key_ == "":
  227. return value
  228. # 如果值为None,返回None(在JavaScript中会跳过)
  229. if value is None:
  230. return None
  231. # 如果值是对象(字典或列表)
  232. if isinstance(value, (dict, list)):
  233. if encryption_enable:
  234. return value # 保持原对象
  235. else:
  236. return json.dumps(value, separators=(',', ':')) # 转为JSON字符串,不带空格
  237. else:
  238. # 其他类型转为字符串
  239. return str(value)
  240. # 递归应用replacer函数
  241. def apply_replacer(obj, parent_key=""):
  242. if isinstance(obj, dict):
  243. result = {}
  244. for k, v in obj.items():
  245. processed_value = replacer(k, v)
  246. if processed_value is not None: # 跳过None值
  247. if isinstance(processed_value, dict):
  248. result[k] = apply_replacer(processed_value, k)
  249. elif isinstance(processed_value, list):
  250. # 对于列表,需要特殊处理
  251. list_result = []
  252. for ii, item in enumerate(processed_value):
  253. if isinstance(item, (dict, list)):
  254. list_result.append(apply_replacer(item, str(ii)))
  255. else:
  256. list_result.append(replacer(str(ii), item))
  257. result[k] = list_result
  258. else:
  259. result[k] = processed_value
  260. return result
  261. else:
  262. return obj
  263. # 应用replacer处理
  264. if isinstance(params, list) and not is_secret:
  265. # 对于数组且非secret情况,u已经是{"str": "..."}格式
  266. processed_obj = u
  267. else:
  268. processed_obj = apply_replacer(u)
  269. # 最后进行JSON序列化再反序列化,使用紧凑格式(无空格)
  270. return json.loads(json.dumps(processed_obj, separators=(',', ':')))
  271. def api_sign(timeout, user_token, params, encryption_enable=True, need_md5=False):
  272. """
  273. 对应JavaScript中的apiSign函数
  274. JavaScript代码分析:
  275. signature: (0,
  276. e.default)("".concat(a).concat(f).concat(s).concat(u).concat("fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb")).toString().toLowerCase()
  277. 参数对应:
  278. a = user_token
  279. f = JSON.stringify(n(params, false, encryption_enable))
  280. s = nonce
  281. u = timestamp
  282. e.default = MD5函数
  283. """
  284. # 计算timestamp: 当前时间加上timeout
  285. current_time = int(time.time() * 1000) # JavaScript中Date.parse返回毫秒
  286. timestamp = str(current_time + timeout)
  287. # timestamp = "1756448767584"
  288. # 生成随机nonce (6位随机数)
  289. # nonce = random.randint(100000, 999999)
  290. nonce = round(1e6 * random.random())
  291. # nonce = 912471
  292. # params = json.dumps(params, separators=(',', ':'))
  293. # 处理参数
  294. # 用于签名字符串计算
  295. f = json.dumps(n(params, False, encryption_enable), separators=(',', ':'), ensure_ascii=False)
  296. # f = json.dumps(n(params, False, encryption_enable), separators=(',', ':'))
  297. logger.info(f"f值:{f}")
  298. # print('f_type:',type(f))
  299. # 用于secretJsonParams
  300. d = json.dumps(n(params, True, encryption_enable), separators=(',', ':'), ensure_ascii=False)
  301. # 拼接签名字符串
  302. signature_string = f"{user_token}{f}{nonce}{timestamp}fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb"
  303. # print("签名字符串:", signature_string)
  304. # print("签名字符串:", type(signature_string))
  305. logger.info(f"待 MD5签名 字符串:{signature_string}")
  306. # signature_string = '{"code":"151C3"}9991231756196851221fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb'
  307. if need_md5:
  308. # 读取JavaScript文件内容
  309. with open('md5_encrypt.js', 'r', encoding='utf-8') as f:
  310. js_code = f.read()
  311. # 编译JavaScript代码
  312. ctx = execjs.compile(js_code)
  313. # 调用r函数
  314. # test_string = '{"banCardFlag":"0","commodityIds":"279","commoditySelectedList":[{"id":"279","commodityName":"收集啦151 惊","commodityCode":"151C3","salesDate":"2025-07-18"}],"pageNum":"8","pageSize":"50"}9124711756448767584fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb'
  315. signature = ctx.call('r', signature_string)
  316. # print(f"JavaScript MD5结果: {signature}")
  317. else:
  318. # 计算MD5签名并转为小写 (对应JavaScript中的e.default)
  319. signature = hashlib.md5(signature_string.encode('utf-8')).hexdigest().lower()
  320. return {
  321. "timestamp": timestamp,
  322. "nonce": nonce,
  323. "signature": signature,
  324. "secretJsonParams": d
  325. }
  326. # --- 使用示例 ---
  327. if __name__ == "__main__":
  328. # 参数加密后: "sWrJmSCVKNjxWn/WaVG9nv7UPCwAwlSvWkYqcX9N9aWXF5LMXegsN0edfLAq5FTTbY5QxpXn1uH/V00F6dSYib3Q9GjFHTbT7Cy7y3fF7frvzsjCW+3lVugoS46XCcUpODM8AhgQcaFrbWZeSDuZO79KGjiMDRmqk6KyCm9olBIU09f3KTGl95QKDSM36ksdgYzboOIb6gdktybRColS/LKk61Nnw/2NPCBNJE+EdXK2dvY5K3CIYomQt+MKamYBBdM4XoPRhAbyPATmSWUZAw=="
  329. # response加密后:
  330. # encrypted_11 = '1 Doxhb1drnjJaHMMC2uECUlgJKzHJrbOas+D7s4skML6PHHk1U+igtdDgZ4OwcpS4n/gOJnuJNkpKPK5ihkyjwoi70YfZHj5tpE0XtjVrVG1Svua1kULJHVOInLgXKm1iEjuwcfNsSvinVRADtuSalKTveIx1RuRcb5R3sGKLgd/VMlN21UaRZoJtOBpUFJy4kkI256GCP1G1Les3m0Z264bBs5o3Ux3VzhgdqbTISRZMmYGZsCObUzL310S9cnPi7FlX5U5VNUSGRMnn2YlkR1UkRl8wM2Jxfmjz4iUPwLErhD5Tn3c7hevw0fvQJTE9k28oEKxTr6cRoN0iDjsS2Fg9wxHj3lV4qdiDgOIkuTj4tniy53WwkmgnMN1FI9ibBHoTrqxMaNXkHaM50VgfQ6+c050n9QNIfO9mcvbGzBBDC6/Y800NPyXEUlyqLulMvDbpQ8NbWg9uH61qSbp4derg32cgawN0nJUSYR1Rk0bB6wV4jgc0eLQzxRQ/LXJlERhids9OvO/Ec0sKEv906MrEJ8j6OQ9vpgqwkw4H7hS4LVzD/NKIqxNDt5ZuMnFnIsjamvpRuPFL8t1xFsu1lAJ8yZyPpCAbGCqzPPt0IYYZVwsNmMGGjrhNKj268yO4vV48H5NGEveyYapDmY8Fd07HAEK6eDNFTIhd3JCgII91K85PqAPYHSO2b3Fnu/PNU/2 Nt9SJOo610Yu4e12O2WBoawzZk+LCPaOax8yqtJgtTJhXUTfwQKm087Lc9vyazpFO/qKewOMXnK1fe1nZUrtm1U93klK9AJQZkX/n55WmjZ3HC9JHzesFbnL+2 PGmaHWmuGxrg56+UyL/7 Lw4etJwPbz05E7ESiX67RvxY9nalBRcDDdzSYAiLb7OcyaRqge4nmD0g8RNWoWuzB5gtR0JkdR41zM9WD+uvzxt915l2+5 Q6GHyiSTPUnUxa76Vg3kGwjX+7 TRztcBdvDEnXNu/ZzbTprr74cgOJTrJ76YzjNkizkPGAn7GTPda3tkZPv4h9AHGtpn03UfE1u9DXHtfaaM9lJizd7b4umcAx91RQwaBvZIBVbqk5qHUQLnmDE0WMhusR1BJzWY7Ue5VdVwbPHRK+zgxACV66yIVjnT9iip8IfEVtU8wK7XTKSCn6OIGmO1QxOq3HE6m3iywTL4bASSX2+T+3 SgOqL8gPNt67foK52h4igfDqQwvNa0pL6/9 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'
  331. #
  332. # decrypted_data = aes_decrypt(encrypted_11)
  333. # 要加密的原始数据
  334. original_data = {
  335. "banCardFlag": "0",
  336. "commodityIds": "279",
  337. "commoditySelectedList": [
  338. {
  339. "id": "279",
  340. "commodityName": "收集啦151 惊",
  341. "commodityCode": "151C3",
  342. "salesDate": "2025-07-18"
  343. }
  344. ],
  345. "pageNum": "5",
  346. "pageSize": "50"
  347. }
  348. print("原始数据:", original_data)
  349. # # 方式1: 使用类实例
  350. # aes_tool = PokemonAESUtil()
  351. #
  352. # # 加密
  353. # encrypted = aes_tool.encrypt(original_data)
  354. # print("加密后 (Base64):", encrypted)
  355. #
  356. # # 解密
  357. # decrypted = aes_tool.decrypt(encrypted)
  358. # print("解密后:", decrypted)
  359. #
  360. # # 验证
  361. # assert original_data == decrypted, "加密/解密失败!"
  362. # print("方式1: 加密和解密成功,数据一致。")
  363. #
  364. # print("-" * 50)
  365. #
  366. # # 方式2: 使用便捷函数
  367. # encrypted2 = pokemon_aes_encrypt(original_data)
  368. # decrypted2 = pokemon_aes_decrypt(encrypted2)
  369. #
  370. # assert original_data == decrypted2, "加密/解密失败!"
  371. # print("方式2: 加密和解密成功,数据一致。")
  372. #
  373. # print("-" * 50)
  374. #
  375. # # 字符串加密示例
  376. # text_data = "Hello, Pokemon!"
  377. # encrypted_text = pokemon_aes_encrypt(text_data)
  378. # decrypted_text = pokemon_aes_decrypt(encrypted_text)
  379. #
  380. # assert text_data == decrypted_text, "字符串加密/解密失败!"
  381. # print(f"字符串加密解密示例: '{text_data}' -> '{decrypted_text}'")
  382. par = {"banCardFlag": "0", "commodityIds": "279", "commoditySelectedList": [
  383. {"id": "279", "commodityName": "收集啦151 惊", "commodityCode": "151C3", "salesDate": "2025-07-18"}],
  384. "pageNum": "8", "pageSize": "50"}
  385. # 浏览器结果:113530048412e1bfd9f69b6c39440908
  386. # 源码中的字符串:'{"banCardFlag":"0","commodityIds":"279","commoditySelectedList":[{"id":"279","commodityName":"收集啦151 惊","commodityCode":"151C3","salesDate":"2025-07-18"}],"pageNum":"8","pageSize":"50"}9124711756448767584fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb'
  387. # 代码中打印的: {"banCardFlag":"0","commodityIds":"279","commoditySelectedList":[{"id":"279","commodityName":"收集啦151 惊","commodityCode":"151C3","salesDate":"2025-07-18"}],"pageNum":"8","pageSize":"50"}9124711756448767584fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb
  388. sign_result = api_sign(
  389. timeout=584,
  390. user_token="",
  391. params=par
  392. )
  393. print(sign_result)