| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- """
- API Key 加密/解密工具(纯标准库,无第三方依赖)
- 算法:XOR 密钥流 + 字节循环位移 + Base64 URL-safe 编码
- 加密流程:
- 1. 用 SECRET_KEY 通过 SHA-256 派生密钥流(hashlib,标准库)
- 2. 明文每字节与密钥流对应字节 XOR
- 3. 每字节循环左移 (i % 5 + 1) 位
- 4. Base64 URL-safe 编码(无 = 填充)
- 解密流程(完全对称,逆序):
- 1. Base64 URL-safe 解码
- 2. 每字节循环右移 (i % 5 + 1) 位
- 3. 与相同密钥流 XOR
- 4. UTF-8 解码
- 解密示例(JavaScript):
- 见文件末尾注释
- """
- from __future__ import annotations
- import base64
- import hashlib
- import os
- from typing import Optional
- def _derive_keystream(key: str, length: int) -> bytes:
- """
- 从 key 派生指定长度的密钥流。
- 原理:反复对 key + 块索引 做 SHA-256,拼接直到够用。
- """
- stream = bytearray()
- block = 0
- key_bytes = key.encode("utf-8")
- while len(stream) < length:
- h = hashlib.sha256(key_bytes + block.to_bytes(4, "big")).digest()
- stream.extend(h)
- block += 1
- return bytes(stream[:length])
- def _rotl8(byte: int, n: int) -> int:
- """8 位循环左移 n 位。"""
- n = n % 8
- return ((byte << n) | (byte >> (8 - n))) & 0xFF
- def _rotr8(byte: int, n: int) -> int:
- """8 位循环右移 n 位。"""
- n = n % 8
- return ((byte >> n) | (byte << (8 - n))) & 0xFF
- def _get_key() -> str:
- key = os.environ.get("APIKEY_ENCRYPT_KEY", "")
- if not key:
- raise RuntimeError("APIKEY_ENCRYPT_KEY 未配置")
- return key
- def encrypt_api_key(plaintext: str) -> str:
- """将 API Key 明文加密为 Base64 URL-safe 密文字符串。"""
- data = plaintext.encode("utf-8")
- keystream = _derive_keystream(_get_key(), len(data))
- result = bytearray(len(data))
- for i, byte in enumerate(data):
- xored = byte ^ keystream[i]
- result[i] = _rotl8(xored, i % 5 + 1)
- return base64.urlsafe_b64encode(result).rstrip(b"=").decode("ascii")
- def decrypt_api_key(ciphertext: str) -> str:
- """将 Base64 URL-safe 密文解密为 API Key 明文。"""
- # 补回 Base64 padding
- rem = len(ciphertext) % 4
- if rem:
- ciphertext += "=" * (4 - rem)
- data = base64.urlsafe_b64decode(ciphertext)
- keystream = _derive_keystream(_get_key(), len(data))
- result = bytearray(len(data))
- for i, byte in enumerate(data):
- unshifted = _rotr8(byte, i % 5 + 1)
- result[i] = unshifted ^ keystream[i]
- return result.decode("utf-8")
- def try_encrypt(plaintext: Optional[str]) -> Optional[str]:
- """安全封装:None 直接返回 None,加密失败返回 None。"""
- if not plaintext:
- return None
- try:
- return encrypt_api_key(plaintext)
- except Exception:
- return None
- # ─────────────────────────────────────────────────────────────────────────────
- # JavaScript 解密实现(供调用方参考)
- # ─────────────────────────────────────────────────────────────────────────────
- #
- # async function deriveKeystream(key, length) {
- # const enc = new TextEncoder();
- # const keyBytes = enc.encode(key);
- # const stream = [];
- # let block = 0;
- # while (stream.length < length) {
- # const blockBytes = new Uint8Array(4);
- # new DataView(blockBytes.buffer).setUint32(0, block, false); // big-endian
- # const input = new Uint8Array([...keyBytes, ...blockBytes]);
- # const hash = await crypto.subtle.digest('SHA-256', input);
- # stream.push(...new Uint8Array(hash));
- # block++;
- # }
- # return stream.slice(0, length);
- # }
- #
- # function rotr8(byte, n) {
- # n = n % 8;
- # return ((byte >>> n) | (byte << (8 - n))) & 0xFF;
- # }
- #
- # function base64urlDecode(str) {
- # str = str.replace(/-/g, '+').replace(/_/g, '/');
- # while (str.length % 4) str += '=';
- # return Uint8Array.from(atob(str), c => c.charCodeAt(0));
- # }
- #
- # async function decryptApiKey(ciphertext, secretKey) {
- # const data = base64urlDecode(ciphertext);
- # const keystream = await deriveKeystream(secretKey, data.length);
- # const result = new Uint8Array(data.length);
- # for (let i = 0; i < data.length; i++) {
- # const unshifted = rotr8(data[i], i % 5 + 1);
- # result[i] = unshifted ^ keystream[i];
- # }
- # return new TextDecoder().decode(result);
- # }
- #
- # // 使用示例:
- # // const apiKey = await decryptApiKey(encryptedValue, 'your_secret_key');
- #
- # ─────────────────────────────────────────────────────────────────────────────
- # Python 解密示例(调用方如果用 Python)
- # ─────────────────────────────────────────────────────────────────────────────
- #
- # import base64, hashlib
- #
- # def decrypt_api_key(ciphertext: str, secret_key: str) -> str:
- # rem = len(ciphertext) % 4
- # if rem:
- # ciphertext += '=' * (4 - rem)
- # data = base64.urlsafe_b64decode(ciphertext)
- # # 派生密钥流
- # stream, block = bytearray(), 0
- # key_bytes = secret_key.encode()
- # while len(stream) < len(data):
- # stream.extend(hashlib.sha256(key_bytes + block.to_bytes(4, 'big')).digest())
- # block += 1
- # keystream = bytes(stream[:len(data)])
- # # 解密
- # result = bytearray(len(data))
- # for i, byte in enumerate(data):
- # n = i % 5 + 1
- # unshifted = ((byte >> n) | (byte << (8 - n))) & 0xFF
- # result[i] = unshifted ^ keystream[i]
- # return result.decode('utf-8')
|