72 lines
1.8 KiB
Python
72 lines
1.8 KiB
Python
"""
|
|
Creator platform signing (XYW_ prefix) for creator.xiaohongshu.com
|
|
|
|
Algorithm: MD5(api + JSON(data)) → AES-128-CBC encrypt → XYW_ prefix
|
|
Much simpler than main API signing.
|
|
|
|
Ported from: ~/readers/redbook/src/lib/creator-signing.ts
|
|
"""
|
|
|
|
import base64
|
|
import hashlib
|
|
import json
|
|
import time
|
|
|
|
from Crypto.Cipher import AES
|
|
from Crypto.Util.Padding import pad
|
|
|
|
AES_KEY = b"7cc4adla5ay0701v"
|
|
AES_IV = b"4uzjr7mbsibcaldp"
|
|
|
|
|
|
def _aes_encrypt(data: str) -> str:
|
|
"""AES-128-CBC encrypt and return hex string."""
|
|
cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
|
|
padded = pad(data.encode("utf-8"), AES.block_size)
|
|
encrypted = cipher.encrypt(padded)
|
|
return encrypted.hex()
|
|
|
|
|
|
def sign_creator(
|
|
api: str,
|
|
data: dict | None,
|
|
a1: str,
|
|
) -> dict[str, str]:
|
|
"""
|
|
Generate creator platform signature headers.
|
|
|
|
Args:
|
|
api: The API path, prefixed with "url=" (e.g., "url=/web_api/sns/v2/note")
|
|
data: Request body data (None for GET requests)
|
|
a1: The a1 cookie value
|
|
|
|
Returns:
|
|
Dict with x-s and x-t headers
|
|
"""
|
|
content = api
|
|
if data is not None:
|
|
content += json.dumps(data, separators=(",", ":"), ensure_ascii=False)
|
|
|
|
x1 = hashlib.md5(content.encode("utf-8")).hexdigest()
|
|
x2 = "0|0|0|1|0|0|1|0|0|0|1|0|0|0|0|1|0|0|0"
|
|
x3 = a1
|
|
x4 = int(time.time() * 1000)
|
|
|
|
plaintext = f"x1={x1};x2={x2};x3={x3};x4={x4};"
|
|
payload = _aes_encrypt(base64.b64encode(plaintext.encode("utf-8")).decode("utf-8"))
|
|
|
|
envelope = {
|
|
"signSvn": "56",
|
|
"signType": "x2",
|
|
"appId": "ugc",
|
|
"signVersion": "1",
|
|
"payload": payload,
|
|
}
|
|
|
|
xs = "XYW_" + base64.b64encode(json.dumps(envelope, separators=(",", ":")).encode("utf-8")).decode("utf-8")
|
|
|
|
return {
|
|
"x-s": xs,
|
|
"x-t": str(x4),
|
|
}
|