# -*- coding: utf-8 -*- # Author : Charley # Python : 3.10.8 # Date : 2025/8/26 14:31 from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import json import base64 import hashlib import time import random import execjs from loguru import logger # def _define_property(obj, key, value): # """ # 模拟JavaScript中的_defineProperty函数 # 相当于 Object.defineProperty(obj, key, {value: value, enumerable: true, configurable: true, writable: true}) # 简单来说就是给对象添加一个可枚举的属性 # """ # # 在Python中,这相当于直接给字典添加键值对 # result = {} # if isinstance(obj, dict): # result.update(obj) # result[key] = value # return result # # # def process_encrypted_params(params, method="POST", encryption_enable=True): # """ # 处理加密参数,模拟JavaScript中的逻辑 # # P = s.default.aesEncrypt(A), # E = this.encryptionEnable ? "{}" == A ? {} : n({}, "GET" == o ? "encryptionUrlParams" : "encryptionBodyParams", "GET" == o ? encodeURIComponent(P) : P) : i, # """ # if not encryption_enable: # return params # # # 将参数转换为JSON字符串 (A) # if isinstance(params, dict) and not params: # 空字典 # A = "{}" # elif params is None: # A = "{}" # else: # A = json.dumps(params, separators=(',', ':'), ensure_ascii=False) # # # 如果是空对象,返回空字典 # if A == "{}": # return {} # # # 进行AES加密得到 P # P = pokemon_aes_encrypt(A) # # # 根据HTTP方法确定参数名和是否URL编码 # if method.upper() == "GET": # param_name = "encryptionUrlParams" # param_value = quote(P, safe='') if P else P # # else: # param_name = "encryptionBodyParams" # param_value = P # # # 使用_define_property创建最终对象 E # E = _define_property({}, param_name, param_value) # # return E class PokemonAESUtil: """宝可梦卡牌数据AES加解密工具类""" def __init__(self): # 固定的密钥字符串 KEY_STRING = "Njda*7^%1<.)0=+u&%hkfs;k" # 将密钥字符串转换为字节 self.key_bytes = KEY_STRING.encode('utf-8') # CryptoJS 在 keySize 未明确指定或不标准时,会使用密钥字节的前 16/24/32 字节 # 由于密钥是 24 字节,使用 AES-192 (24 字节密钥) if len(self.key_bytes) < 24: # 用 null 字节填充到 24 字节 self.key_bytes = self.key_bytes.ljust(24, b'\0') elif len(self.key_bytes) > 24: # 截取前 24 字节 self.key_bytes = self.key_bytes[:24] def encrypt(self, data): """ AES ECB 模式加密,PKCS7 填充 :param data: 要加密的数据 (字典或字符串) :return: Base64 编码的密文字符串 """ # 创建 AES ECB 模式加密器 cipher = AES.new(self.key_bytes, AES.MODE_ECB) # 处理输入数据 if isinstance(data, dict): # 如果是字典,转换为紧凑格式的JSON字符串 plaintext = json.dumps(data, separators=(',', ':')) else: # 如果是字符串,直接使用 plaintext = str(data) # 将明文字符串编码为 UTF-8 字节 plaintext_bytes = plaintext.encode('utf-8') # 使用 PKCS7 填充 padded_plaintext = pad(plaintext_bytes, AES.block_size) # 执行加密 ciphertext_bytes = cipher.encrypt(padded_plaintext) # 将密文字节进行 Base64 编码并转换为字符串 ciphertext_b64 = base64.b64encode(ciphertext_bytes).decode('utf-8') return ciphertext_b64 def decrypt(self, ciphertext_b64): """ AES ECB 模式解密,PKCS7 去填充 :param ciphertext_b64: Base64 编码的密文字符串 :return: 解密后的原始对象 (通常是字典) """ # 创建 AES ECB 模式解密器 cipher = AES.new(self.key_bytes, AES.MODE_ECB) # 将 Base64 字符串解码为字节 ciphertext_bytes = base64.b64decode(ciphertext_b64) # 执行解密 padded_plaintext_bytes = cipher.decrypt(ciphertext_bytes) # 移除 PKCS7 填充 plaintext_bytes = unpad(padded_plaintext_bytes, AES.block_size) # 将字节解码为 UTF-8 字符串 plaintext = plaintext_bytes.decode('utf-8') # 尝试解析 JSON 字符串为 Python 对象,如果失败则返回原始字符串 try: return json.loads(plaintext) except json.JSONDecodeError: return plaintext # 创建全局实例,方便直接调用 aes_util = PokemonAESUtil() # 便捷函数,可以直接调用 def pokemon_aes_encrypt(data): """ 便捷加密函数 :param data: 要加密的数据 (字典或字符串) :return: Base64 编码的密文字符串 """ return aes_util.encrypt(data) def pokemon_aes_decrypt(ciphertext_b64): """ 便捷解密函数 :param ciphertext_b64: Base64 编码的密文字符串 :return: 解密后的原始对象 """ return aes_util.decrypt(ciphertext_b64) """ function n(e, r, t) { var n = new Array , a = 0; for (var i in e) n[a] = i, a++; var o = n.sort() , u = {}; for (var s in o) u[o[s]] = e[o[s]]; if (Array.isArray(e)) { if (r) return JSON.parse(JSON.stringify(e)); u = { str: JSON.stringify(e) } } return JSON.parse(JSON.stringify(u, (function(e, r) { if (e) { if (null == r || null == r) return; return r instanceof Object ? t ? r : JSON.stringify(r) : "" + r } return r } ))) } """ def n(params, is_secret, encryption_enable): """ 模拟JavaScript中的n函数 参数说明: params: 要处理的参数对象 (对应JavaScript中的e) is_secret: 是否为secret处理 (对应JavaScript中的r) encryption_enable: 是否启用加密 (对应JavaScript中的t) JavaScript源码分析: 1. 对对象的键进行排序 2. 如果是数组,根据is_secret决定处理方式 3. 使用自定义序列化函数处理所有值 """ # 如果params是None,直接返回 if params is None: return params # 初始化变量(模拟JavaScript变量声明) keys_array = [] index = 0 # 模拟 for (var i in e) 循环,提取所有键 # 注意:对于数组,键是索引(0, 1, 2, ...) if isinstance(params, (dict, list)): if isinstance(params, dict): # 对于字典,提取键 for key in params: keys_array.append(key) index += 1 else: # 对于数组,提取索引作为键 for i in range(len(params)): keys_array.append(str(i)) index += 1 # 排序键 sorted_keys = sorted(keys_array) # 创建新对象 u,按键的排序顺序重建 u = {} if isinstance(params, dict): for key in sorted_keys: u[key] = params[key] elif isinstance(params, list): for key in sorted_keys: # key是字符串索引,需要转为整数 idx = int(key) u[key] = params[idx] # 检查是否为数组 if isinstance(params, list): if is_secret: return json.loads(json.dumps(params, separators=(',', ':'))) else: u = { "str": json.dumps(params, separators=(',', ':')) } # 自定义序列化函数(模拟JavaScript的replacer函数) def replacer(key_, value): # key为空字符串表示是根对象 if key_ == "": return value # 如果值为None,返回None(在JavaScript中会跳过) if value is None: return None # 如果值是对象(字典或列表) if isinstance(value, (dict, list)): if encryption_enable: return value # 保持原对象 else: return json.dumps(value, separators=(',', ':')) # 转为JSON字符串,不带空格 else: # 其他类型转为字符串 return str(value) # 递归应用replacer函数 def apply_replacer(obj, parent_key=""): if isinstance(obj, dict): result = {} for k, v in obj.items(): processed_value = replacer(k, v) if processed_value is not None: # 跳过None值 if isinstance(processed_value, dict): result[k] = apply_replacer(processed_value, k) elif isinstance(processed_value, list): # 对于列表,需要特殊处理 list_result = [] for ii, item in enumerate(processed_value): if isinstance(item, (dict, list)): list_result.append(apply_replacer(item, str(ii))) else: list_result.append(replacer(str(ii), item)) result[k] = list_result else: result[k] = processed_value return result else: return obj # 应用replacer处理 if isinstance(params, list) and not is_secret: # 对于数组且非secret情况,u已经是{"str": "..."}格式 processed_obj = u else: processed_obj = apply_replacer(u) # 最后进行JSON序列化再反序列化,使用紧凑格式(无空格) return json.loads(json.dumps(processed_obj, separators=(',', ':'))) def api_sign(timeout, user_token, params, encryption_enable=True, need_md5=False): """ 对应JavaScript中的apiSign函数 JavaScript代码分析: signature: (0, e.default)("".concat(a).concat(f).concat(s).concat(u).concat("fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb")).toString().toLowerCase() 参数对应: a = user_token f = JSON.stringify(n(params, false, encryption_enable)) s = nonce u = timestamp e.default = MD5函数 """ # 计算timestamp: 当前时间加上timeout current_time = int(time.time() * 1000) # JavaScript中Date.parse返回毫秒 timestamp = str(current_time + timeout) # timestamp = "1756448767584" # 生成随机nonce (6位随机数) # nonce = random.randint(100000, 999999) nonce = round(1e6 * random.random()) # nonce = 912471 # params = json.dumps(params, separators=(',', ':')) # 处理参数 # 用于签名字符串计算 f = json.dumps(n(params, False, encryption_enable), separators=(',', ':'), ensure_ascii=False) # f = json.dumps(n(params, False, encryption_enable), separators=(',', ':')) logger.info(f"f值:{f}") # print('f_type:',type(f)) # 用于secretJsonParams d = json.dumps(n(params, True, encryption_enable), separators=(',', ':'), ensure_ascii=False) # 拼接签名字符串 signature_string = f"{user_token}{f}{nonce}{timestamp}fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb" # print("签名字符串:", signature_string) # print("签名字符串:", type(signature_string)) logger.info(f"待 MD5签名 字符串:{signature_string}") # signature_string = '{"code":"151C3"}9991231756196851221fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb' if need_md5: # 读取JavaScript文件内容 with open('md5_encrypt.js', 'r', encoding='utf-8') as f: js_code = f.read() # 编译JavaScript代码 ctx = execjs.compile(js_code) # 调用r函数 # test_string = '{"banCardFlag":"0","commodityIds":"279","commoditySelectedList":[{"id":"279","commodityName":"收集啦151 惊","commodityCode":"151C3","salesDate":"2025-07-18"}],"pageNum":"8","pageSize":"50"}9124711756448767584fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb' signature = ctx.call('r', signature_string) # print(f"JavaScript MD5结果: {signature}") else: # 计算MD5签名并转为小写 (对应JavaScript中的e.default) signature = hashlib.md5(signature_string.encode('utf-8')).hexdigest().lower() return { "timestamp": timestamp, "nonce": nonce, "signature": signature, "secretJsonParams": d } # --- 使用示例 --- if __name__ == "__main__": # 参数加密后: "sWrJmSCVKNjxWn/WaVG9nv7UPCwAwlSvWkYqcX9N9aWXF5LMXegsN0edfLAq5FTTbY5QxpXn1uH/V00F6dSYib3Q9GjFHTbT7Cy7y3fF7frvzsjCW+3lVugoS46XCcUpODM8AhgQcaFrbWZeSDuZO79KGjiMDRmqk6KyCm9olBIU09f3KTGl95QKDSM36ksdgYzboOIb6gdktybRColS/LKk61Nnw/2NPCBNJE+EdXK2dvY5K3CIYomQt+MKamYBBdM4XoPRhAbyPATmSWUZAw==" # response加密后: # 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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' # # decrypted_data = aes_decrypt(encrypted_11) # 要加密的原始数据 original_data = { "banCardFlag": "0", "commodityIds": "279", "commoditySelectedList": [ { "id": "279", "commodityName": "收集啦151 惊", "commodityCode": "151C3", "salesDate": "2025-07-18" } ], "pageNum": "5", "pageSize": "50" } print("原始数据:", original_data) # # 方式1: 使用类实例 # aes_tool = PokemonAESUtil() # # # 加密 # encrypted = aes_tool.encrypt(original_data) # print("加密后 (Base64):", encrypted) # # # 解密 # decrypted = aes_tool.decrypt(encrypted) # print("解密后:", decrypted) # # # 验证 # assert original_data == decrypted, "加密/解密失败!" # print("方式1: 加密和解密成功,数据一致。") # # print("-" * 50) # # # 方式2: 使用便捷函数 # encrypted2 = pokemon_aes_encrypt(original_data) # decrypted2 = pokemon_aes_decrypt(encrypted2) # # assert original_data == decrypted2, "加密/解密失败!" # print("方式2: 加密和解密成功,数据一致。") # # print("-" * 50) # # # 字符串加密示例 # text_data = "Hello, Pokemon!" # encrypted_text = pokemon_aes_encrypt(text_data) # decrypted_text = pokemon_aes_decrypt(encrypted_text) # # assert text_data == decrypted_text, "字符串加密/解密失败!" # print(f"字符串加密解密示例: '{text_data}' -> '{decrypted_text}'") par = {"banCardFlag": "0", "commodityIds": "279", "commoditySelectedList": [ {"id": "279", "commodityName": "收集啦151 惊", "commodityCode": "151C3", "salesDate": "2025-07-18"}], "pageNum": "8", "pageSize": "50"} # 浏览器结果:113530048412e1bfd9f69b6c39440908 # 源码中的字符串:'{"banCardFlag":"0","commodityIds":"279","commoditySelectedList":[{"id":"279","commodityName":"收集啦151 惊","commodityCode":"151C3","salesDate":"2025-07-18"}],"pageNum":"8","pageSize":"50"}9124711756448767584fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb' # 代码中打印的: {"banCardFlag":"0","commodityIds":"279","commoditySelectedList":[{"id":"279","commodityName":"收集啦151 惊","commodityCode":"151C3","salesDate":"2025-07-18"}],"pageNum":"8","pageSize":"50"}9124711756448767584fWS21MVyxkYwEoCIAHieg7Tqn0jPl3GzQvRsDJcb sign_result = api_sign( timeout=584, user_token="", params=par ) print(sign_result)