127 lines
4.5 KiB
Python
127 lines
4.5 KiB
Python
"""
|
|
Cookie Cloud 客户端(独立前端使用)
|
|
"""
|
|
import json
|
|
import hashlib
|
|
import logging
|
|
import requests
|
|
from base64 import b64decode
|
|
|
|
logger = logging.getLogger('cookie_cloud')
|
|
|
|
try:
|
|
from Cryptodome.Cipher import AES
|
|
from Cryptodome.Util.Padding import unpad
|
|
except ImportError:
|
|
from Crypto.Cipher import AES
|
|
from Crypto.Util.Padding import unpad
|
|
|
|
|
|
def cookie_decrypt(uuid: str, encrypted_b64: str, password: str) -> dict:
|
|
"""解密Cookie Cloud数据"""
|
|
the_key = hashlib.md5(f"{uuid}-{password}".encode()).hexdigest()[:16].encode()
|
|
encrypted_data = b64decode(encrypted_b64)
|
|
|
|
logger.info("尝试AES-ECB解密...")
|
|
try:
|
|
cipher = AES.new(the_key, AES.MODE_ECB)
|
|
decrypted = cipher.decrypt(encrypted_data)
|
|
result = unpad(decrypted, AES.block_size)
|
|
data = json.loads(result.decode('utf-8'))
|
|
logger.info("AES-ECB解密成功")
|
|
return data
|
|
except Exception as e:
|
|
logger.info(f"AES-ECB解密失败: {e},尝试Salted__格式...")
|
|
pass
|
|
|
|
if encrypted_data[:8] == b'Salted__':
|
|
logger.info("检测到Salted__格式,尝试CBC解密...")
|
|
salt = encrypted_data[8:16]
|
|
ciphertext = encrypted_data[16:]
|
|
logger.info(f"Salt: {salt.hex()}")
|
|
d = [b'']
|
|
while len(b''.join(d)) < 48:
|
|
d.append(hashlib.md5(d[-1] + the_key + salt).digest())
|
|
key_iv = b''.join(d)
|
|
cipher = AES.new(key_iv[:32], AES.MODE_CBC, key_iv[32:48])
|
|
decrypted = unpad(cipher.decrypt(ciphertext), AES.block_size)
|
|
data = json.loads(decrypted.decode('utf-8'))
|
|
logger.info("CBC解密成功")
|
|
return data
|
|
|
|
logger.error("所有解密方式均失败")
|
|
raise ValueError('无法解密Cookie数据')
|
|
|
|
|
|
def download_cookies(api_url: str, uuid: str, password: str) -> dict:
|
|
"""从Cookie Cloud服务器下载并解密Cookie"""
|
|
api_url = api_url.rstrip('/')
|
|
url = f"{api_url}/get/{uuid}"
|
|
|
|
logger.info(f"[1/3] 请求Cookie Cloud: {url}")
|
|
logger.info(f"[1/3] UUID: {uuid}")
|
|
|
|
response = requests.get(url, params={'password': password}, timeout=10)
|
|
response.raise_for_status()
|
|
result = response.json()
|
|
|
|
logger.info(f"[1/3] HTTP状态码: {response.status_code}")
|
|
logger.info(f"[1/3] 返回数据keys: {list(result.keys())}")
|
|
logger.info(f"[1/3] 返回数据大小: {len(json.dumps(result))} bytes")
|
|
|
|
if isinstance(result, dict) and result.get('encrypted'):
|
|
logger.info("[2/3] 检测到加密数据,开始解密...")
|
|
result = cookie_decrypt(uuid, result['encrypted'], password)
|
|
logger.info(f"[2/3] 解密完成,数据类型: {type(result)}")
|
|
if isinstance(result, dict):
|
|
logger.info(f"[2/3] Cookie数据包含域名: {list(result.keys())}")
|
|
|
|
if isinstance(result, dict) and 'cookie_data' in result:
|
|
cookies_data = result.get('cookie_data', {})
|
|
logger.info(f"[3/3] 新版CookieCloud格式,提取cookie_data")
|
|
logger.info(f"[3/3] cookie_data包含域名数: {len(cookies_data)}")
|
|
return cookies_data
|
|
|
|
logger.info(f"[3/3] 返回数据已处理完成")
|
|
return result
|
|
|
|
|
|
def get_cookies_for_domain(cookies_data: dict, domain: str) -> list:
|
|
"""提取指定域名对应的Cookie列表"""
|
|
result = []
|
|
matched_hosts = []
|
|
|
|
domain_parts = domain.lower().split('.')
|
|
|
|
for host, cookie_list in cookies_data.items():
|
|
host_clean = host.lower().lstrip('.')
|
|
domain_clean = domain.lower()
|
|
|
|
is_match = False
|
|
if host_clean == domain_clean:
|
|
is_match = True
|
|
elif host_clean.endswith('.' + domain_clean):
|
|
is_match = True
|
|
elif domain_clean.endswith('.' + host_clean):
|
|
is_match = True
|
|
|
|
if is_match:
|
|
matched_hosts.append(host)
|
|
if isinstance(cookie_list, list):
|
|
for cookie in cookie_list:
|
|
if cookie.get('name') and cookie.get('value'):
|
|
result.append({
|
|
'name': cookie['name'],
|
|
'value': cookie['value'],
|
|
'domain': cookie.get('domain', domain),
|
|
'path': cookie.get('path', '/')
|
|
})
|
|
|
|
logger.info(f"域名匹配结果: 目标={domain}, 匹配hosts={matched_hosts}")
|
|
logger.info(f"提取Cookie数量: {len(result)}")
|
|
if result:
|
|
cookie_names = [c['name'] for c in result[:5]]
|
|
logger.info(f"前5个Cookie名称: {cookie_names}")
|
|
|
|
return result
|