Initial commit
This commit is contained in:
+183
@@ -0,0 +1,183 @@
|
||||
"""Cookie Cloud 模块 - 从 Cookie Cloud 服务器获取并解密 cookie 数据"""
|
||||
import hashlib
|
||||
import json
|
||||
import base64
|
||||
from typing import Dict, List, Optional
|
||||
import requests
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import unpad
|
||||
from applogger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class CookieCloud:
|
||||
"""Cookie Cloud 客户端"""
|
||||
|
||||
def __init__(self, api_url: str, uuid: str, password: str):
|
||||
"""
|
||||
初始化 Cookie Cloud 客户端
|
||||
|
||||
Args:
|
||||
api_url: Cookie Cloud API 地址
|
||||
uuid: 用户 UUID
|
||||
password: 用户密码
|
||||
"""
|
||||
self.api_url = api_url.rstrip('/')
|
||||
self.uuid = uuid
|
||||
self.password = password
|
||||
|
||||
def decrypt(self, encrypted_data: str) -> Dict:
|
||||
"""
|
||||
解密 Cookie Cloud 数据
|
||||
|
||||
Args:
|
||||
encrypted_data: 加密的数据字符串
|
||||
|
||||
Returns:
|
||||
解密后的数据字典,包含 cookie_data 和 local_storage_data
|
||||
"""
|
||||
# 生成解密密钥: md5(uuid+password) 取前 16 个字符
|
||||
key_str = f"{self.uuid}{self.password}"
|
||||
key = hashlib.md5(key_str.encode()).hexdigest()[:16].encode()
|
||||
print(f"生成的密钥: {key}")
|
||||
print(f"密钥长度: {len(key)}")
|
||||
|
||||
# 解码 Base64
|
||||
encrypted = base64.b64decode(encrypted_data)
|
||||
print(f"Base64 解码后长度: {len(encrypted)}")
|
||||
print(f"解码后前 20 字节: {encrypted[:20]}")
|
||||
|
||||
# 检查是否有盐值(OpenSSL 格式)
|
||||
if encrypted.startswith(b'Salted__'):
|
||||
print("检测到 OpenSSL 格式数据")
|
||||
# 对于 OpenSSL 格式,我们需要跳过盐值部分
|
||||
# 但根据用户要求,我们直接使用16字符密钥,不考虑盐值
|
||||
ciphertext = encrypted[16:] # 跳过 "Salted__" 和盐值
|
||||
print(f"密文长度: {len(ciphertext)}")
|
||||
else:
|
||||
print("未检测到 OpenSSL 格式,使用整个数据作为密文")
|
||||
ciphertext = encrypted
|
||||
|
||||
# 使用固定的 IV (16 个 0 字节)
|
||||
iv = b'\x00' * 16
|
||||
print(f"使用的 IV: {iv}")
|
||||
|
||||
# AES-128-CBC 解密(16字节密钥)
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
decrypted = cipher.decrypt(ciphertext)
|
||||
print(f"解密后长度: {len(decrypted)}")
|
||||
print(f"解密后前 100 字节: {decrypted[:100]}")
|
||||
|
||||
try:
|
||||
# 移除 PKCS#7 填充
|
||||
decrypted = unpad(decrypted, AES.block_size)
|
||||
print(f"移除填充后长度: {len(decrypted)}")
|
||||
print(f"移除填充后前 100 字节: {decrypted[:100]}")
|
||||
|
||||
# 尝试解析 JSON
|
||||
result = json.loads(decrypted.decode('utf-8'))
|
||||
print("JSON 解析成功")
|
||||
return result
|
||||
except Exception as e:
|
||||
print(f"解密失败: {e}")
|
||||
# 尝试直接截取可能的有效数据
|
||||
try:
|
||||
# 尝试找到 JSON 开始的位置
|
||||
for i in range(len(decrypted)):
|
||||
try:
|
||||
test_data = decrypted[i:]
|
||||
test_str = test_data.decode('utf-8', errors='ignore')
|
||||
if test_str.strip().startswith('{'):
|
||||
print(f"找到可能的 JSON 开始位置: {i}")
|
||||
result = json.loads(test_str)
|
||||
print("JSON 解析成功(直接截取)")
|
||||
return result
|
||||
except:
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
raise
|
||||
|
||||
def get_cookies(self) -> Dict[str, List[Dict]]:
|
||||
"""
|
||||
从 Cookie Cloud 服务器获取 cookie 数据
|
||||
|
||||
Returns:
|
||||
按域名分组的 cookie 数据字典
|
||||
格式: {"domain.com": [{"name": "cookie1", "value": "val1", ...}, ...], ...}
|
||||
"""
|
||||
url = f"{self.api_url}/get/{self.uuid}"
|
||||
|
||||
try:
|
||||
response = requests.get(url, timeout=30)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
logger.info(f"Cookie Cloud 响应: {json.dumps(data, indent=2)}")
|
||||
|
||||
if not data or 'encrypted' not in data:
|
||||
logger.error("响应中没有 encrypted 字段")
|
||||
return {}
|
||||
|
||||
# 打印完整的加密数据长度和前200个字符
|
||||
logger.info(f"加密数据长度: {len(data['encrypted'])}")
|
||||
logger.info(f"加密数据前 200 个字符: {data['encrypted'][:200]}...")
|
||||
|
||||
try:
|
||||
decrypted_data = self.decrypt(data['encrypted'])
|
||||
logger.info(f"解密后的数据: {json.dumps(decrypted_data, indent=2)}")
|
||||
except Exception as e:
|
||||
logger.error(f"解密失败: {e}")
|
||||
return {}
|
||||
|
||||
# 解析 cookie_data
|
||||
cookie_dict = {}
|
||||
if 'cookie_data' in decrypted_data:
|
||||
for domain, cookies in decrypted_data['cookie_data'].items():
|
||||
cookie_dict[domain] = []
|
||||
for cookie in cookies:
|
||||
# 处理 sameSite 字段
|
||||
if cookie.get('sameSite') == 'unspecified':
|
||||
cookie['sameSite'] = 'Lax'
|
||||
cookie_dict[domain].append(cookie)
|
||||
|
||||
return cookie_dict
|
||||
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"请求 Cookie Cloud 失败: {e}")
|
||||
return {}
|
||||
except Exception as e:
|
||||
logger.error(f"处理 Cookie Cloud 数据失败: {e}")
|
||||
return {}
|
||||
|
||||
def get_cookies_for_domain(self, domain: str) -> List[Dict]:
|
||||
"""
|
||||
获取指定域名的 cookie
|
||||
|
||||
Args:
|
||||
domain: 域名
|
||||
|
||||
Returns:
|
||||
cookie 列表
|
||||
"""
|
||||
all_cookies = self.get_cookies()
|
||||
|
||||
# 查找匹配的域名
|
||||
for cookie_domain, cookies in all_cookies.items():
|
||||
if domain in cookie_domain or cookie_domain in domain:
|
||||
return cookies
|
||||
|
||||
return []
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 测试代码
|
||||
cc = CookieCloud(
|
||||
api_url="https://movie-pilot.org/cookiecloud",
|
||||
uuid="hu6n2vcUmzpu7mqUN2rVCg",
|
||||
password="jtDM5dV9AyqXkZdQVeA9f6"
|
||||
)
|
||||
|
||||
cookies = cc.get_cookies()
|
||||
logger.info(json.dumps(cookies, indent=2, ensure_ascii=False))
|
||||
Reference in New Issue
Block a user