apikey-decrypt.md 8.2 KB

API Key 解密说明

调用 /api/public/prices 接口时,返回数据中每个模型的 api_key 字段是加密后的密文。本文档说明如何在你的应用中解密还原出原始 API Key。


所需信息

项目
加密密钥 25e9e87b18cf40d0ed0f102b8d2ec3a8
算法 XOR 密钥流 + 字节循环位移 + Base64 URL-safe
依赖 无第三方库,纯标准库实现

⚠️ 请妥善保管加密密钥,不要泄露给未授权方。


算法说明

加密流程(服务端)

明文 UTF-8 字节
  → 与密钥流逐字节 XOR
  → 每字节循环左移 (i % 5 + 1) 位
  → Base64 URL-safe 编码(无 = 填充)
  → 密文字符串

解密流程(客户端,完全对称)

密文字符串
  → Base64 URL-safe 解码
  → 每字节循环右移 (i % 5 + 1) 位
  → 与相同密钥流逐字节 XOR
  → UTF-8 解码
  → 原始 API Key

密钥流派生方式

SECRET_KEY + 块索引(4字节大端) 反复做 SHA-256,拼接直到长度够用:

block_0 = SHA-256(key_bytes + 0x00000000)   # 32 字节
block_1 = SHA-256(key_bytes + 0x00000001)   # 32 字节
block_2 = SHA-256(key_bytes + 0x00000002)   # 32 字节
...
keystream = block_0 + block_1 + block_2 + ...  取前 N 字节

各语言解密实现

JavaScript / TypeScript

适用于浏览器和 Node.js(18+),使用内置 crypto.subtle

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 SECRET_KEY = '25e9e87b18cf40d0ed0f102b8d2ec3a8';

const apiKey = await decryptApiKey(model.api_key, SECRET_KEY);
console.log(apiKey); // sk-xxxxxxxxxxxxxxxx

Python

使用标准库 hashlib + base64,无需安装任何包。

import base64
import hashlib


def decrypt_api_key(ciphertext: str, secret_key: str) -> str:
    # 1. Base64 URL-safe 解码
    rem = len(ciphertext) % 4
    if rem:
        ciphertext += '=' * (4 - rem)
    data = base64.urlsafe_b64decode(ciphertext)

    # 2. 派生密钥流
    stream, block = bytearray(), 0
    key_bytes = secret_key.encode('utf-8')
    while len(stream) < len(data):
        stream.extend(
            hashlib.sha256(key_bytes + block.to_bytes(4, 'big')).digest()
        )
        block += 1
    keystream = bytes(stream[:len(data)])

    # 3. 循环右移 + XOR
    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')


# 使用示例
SECRET_KEY = '25e9e87b18cf40d0ed0f102b8d2ec3a8'

api_key = decrypt_api_key(model['api_key'], SECRET_KEY)
print(api_key)  # sk-xxxxxxxxxxxxxxxx

Java

使用 JDK 内置 MessageDigest + Base64,无需第三方依赖。

import java.util.Base64;
import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;

public class ApiKeyDecryptor {

    private static final String SECRET_KEY = "25e9e87b18cf40d0ed0f102b8d2ec3a8";

    private static byte[] deriveKeystream(String key, int length) throws Exception {
        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        byte[] stream = new byte[length];
        int pos = 0, block = 0;

        while (pos < length) {
            sha256.reset();
            sha256.update(keyBytes);
            sha256.update(new byte[]{
                (byte)(block >> 24), (byte)(block >> 16),
                (byte)(block >> 8),  (byte) block
            });
            byte[] hash = sha256.digest();
            int toCopy = Math.min(hash.length, length - pos);
            System.arraycopy(hash, 0, stream, pos, toCopy);
            pos += toCopy;
            block++;
        }
        return stream;
    }

    private static int rotr8(int b, int n) {
        n = n % 8;
        return ((b >>> n) | (b << (8 - n))) & 0xFF;
    }

    public static String decrypt(String ciphertext) throws Exception {
        // 1. Base64 URL-safe 解码
        String padded = ciphertext.replace('-', '+').replace('_', '/');
        while (padded.length() % 4 != 0) padded += "=";
        byte[] data = Base64.getDecoder().decode(padded);

        // 2. 派生密钥流
        byte[] keystream = deriveKeystream(SECRET_KEY, data.length);

        // 3. 循环右移 + XOR
        byte[] result = new byte[data.length];
        for (int i = 0; i < data.length; i++) {
            int unshifted = rotr8(data[i] & 0xFF, i % 5 + 1);
            result[i] = (byte)(unshifted ^ keystream[i]);
        }
        return new String(result, StandardCharsets.UTF_8);
    }

    // 使用示例
    public static void main(String[] args) throws Exception {
        String encrypted = "..."; // 从 API 返回的 api_key 字段
        String apiKey = decrypt(encrypted);
        System.out.println(apiKey); // sk-xxxxxxxxxxxxxxxx
    }
}

Go

package main

import (
    "crypto/sha256"
    "encoding/base64"
    "fmt"
)

const secretKey = "25e9e87b18cf40d0ed0f102b8d2ec3a8"

func deriveKeystream(key string, length int) []byte {
    keyBytes := []byte(key)
    stream := make([]byte, 0, length+32)
    for block := 0; len(stream) < length; block++ {
        blockBytes := []byte{
            byte(block >> 24), byte(block >> 16),
            byte(block >> 8), byte(block),
        }
        h := sha256.Sum256(append(keyBytes, blockBytes...))
        stream = append(stream, h[:]...)
    }
    return stream[:length]
}

func rotr8(b byte, n int) byte {
    n = n % 8
    return (b >> n) | (b << (8 - n))
}

func decryptApiKey(ciphertext string) (string, error) {
    // Base64 URL-safe 解码
    rem := len(ciphertext) % 4
    if rem != 0 {
        for i := 0; i < 4-rem; i++ {
            ciphertext += "="
        }
    }
    data, err := base64.URLEncoding.DecodeString(ciphertext)
    if err != nil {
        return "", err
    }

    keystream := deriveKeystream(secretKey, len(data))
    result := make([]byte, len(data))
    for i, b := range data {
        unshifted := rotr8(b, i%5+1)
        result[i] = unshifted ^ keystream[i]
    }
    return string(result), nil
}

func main() {
    encrypted := "..." // 从 API 返回的 api_key 字段
    apiKey, err := decryptApiKey(encrypted)
    if err != nil {
        panic(err)
    }
    fmt.Println(apiKey) // sk-xxxxxxxxxxxxxxxx
}

完整使用示例

请求 /api/public/prices 后,遍历 models 数组,对每个有 api_key 的模型解密:

const SECRET_KEY = '25e9e87b18cf40d0ed0f102b8d2ec3a8';

const res = await fetch('https://your-api.com/api/public/prices', {
  headers: { 'Referer': 'https://your-domain.com' }
});
const data = await res.json();

for (const model of data.models) {
  if (model.api_key) {
    model.api_key_plain = await decryptApiKey(model.api_key, SECRET_KEY);
  }
}

注意事项

  • api_key 字段为 null 时表示该模型未配置 API Key,无需解密
  • 密钥更换后,旧密文将无法解密,需重新请求接口获取新密文
  • 建议将 SECRET_KEY 存储在环境变量中,不要硬编码在源码里