From 25ab1575eda98d9e5eb2f08e33b30bdd05762265 Mon Sep 17 00:00:00 2001 From: shaw Date: Sat, 26 Jul 2025 13:56:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=B9=E8=BF=9B=20Gemini=20token=20?= =?UTF-8?q?=E5=88=B7=E6=96=B0=E6=B5=8B=E8=AF=95=E8=84=9A=E6=9C=AC=E5=92=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A7=A3=E5=AF=86=E6=B5=8B=E8=AF=95=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正加密盐值为 'gemini-account-salt'(之前错误使用了其他值) - 在 test-gemini-refresh.js 中添加详细的调试信息 - 显示配置信息和加密参数 - 显示原始加密数据 - 尝试手动解密并显示结果 - 更详细的错误信息 - 添加 test-gemini-decrypt.js 用于单独测试解密功能 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- scripts/test-gemini-decrypt.js | 102 +++++++++++++++++++++++++++++++++ scripts/test-gemini-refresh.js | 59 ++++++++++++++++++- 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 scripts/test-gemini-decrypt.js diff --git a/scripts/test-gemini-decrypt.js b/scripts/test-gemini-decrypt.js new file mode 100644 index 00000000..8a6ff5e2 --- /dev/null +++ b/scripts/test-gemini-decrypt.js @@ -0,0 +1,102 @@ +#!/usr/bin/env node + +/** + * 测试 Gemini 账户解密 + */ + +const path = require('path'); +const dotenv = require('dotenv'); +const crypto = require('crypto'); + +// 加载环境变量 +dotenv.config({ path: path.join(__dirname, '..', '.env') }); + +const redis = require('../src/models/redis'); +const config = require('../config/config'); + +const ALGORITHM = 'aes-256-cbc'; +const ENCRYPTION_SALT = 'gemini-account-salt'; // 正确的盐值! +const GEMINI_ACCOUNT_KEY_PREFIX = 'gemini_account:'; + +// 生成加密密钥(与 geminiAccountService 完全相同) +function generateEncryptionKey() { + return crypto.scryptSync(config.security.encryptionKey, ENCRYPTION_SALT, 32); +} + +// 解密函数(与 geminiAccountService 相同) +function decrypt(text) { + if (!text) return ''; + try { + const key = generateEncryptionKey(); + // IV 是固定长度的 32 个十六进制字符(16 字节) + const ivHex = text.substring(0, 32); + const encryptedHex = text.substring(33); // 跳过冒号 + + const iv = Buffer.from(ivHex, 'hex'); + const encryptedText = Buffer.from(encryptedHex, 'hex'); + const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); + let decrypted = decipher.update(encryptedText); + decrypted = Buffer.concat([decrypted, decipher.final()]); + return decrypted.toString(); + } catch (error) { + console.error('解密错误:', error.message); + return null; + } +} + +async function testDecrypt() { + try { + console.log('🚀 测试 Gemini 账户解密...\n'); + + console.log('📋 加密配置:'); + console.log(` config.security.encryptionKey: ${config.security.encryptionKey}`); + console.log(` ENCRYPTION_SALT: ${ENCRYPTION_SALT}`); + console.log(); + + // 连接 Redis + console.log('📡 连接 Redis...'); + await redis.connect(); + console.log('✅ Redis 连接成功\n'); + + const client = redis.getClient(); + const keys = await client.keys(`${GEMINI_ACCOUNT_KEY_PREFIX}*`); + + if (keys.length === 0) { + console.log('❌ 没有找到 Gemini 账户'); + process.exit(1); + } + + console.log(`🔍 找到 ${keys.length} 个 Gemini 账户\n`); + + for (const key of keys) { + const accountData = await client.hgetall(key); + const accountId = key.replace(GEMINI_ACCOUNT_KEY_PREFIX, ''); + + console.log(`📋 账户: ${accountData.name} (${accountId})`); + + if (accountData.refreshToken) { + console.log('🔐 尝试解密 refreshToken...'); + const decrypted = decrypt(accountData.refreshToken); + + if (decrypted) { + console.log('✅ 解密成功!'); + console.log(` Token 前缀: ${decrypted.substring(0, 20)}...`); + } else { + console.log('❌ 解密失败'); + } + } else { + console.log('⚠️ 无 refreshToken'); + } + + console.log(); + } + + } catch (error) { + console.error('❌ 测试失败:', error); + } finally { + await redis.disconnect(); + process.exit(0); + } +} + +testDecrypt(); \ No newline at end of file diff --git a/scripts/test-gemini-refresh.js b/scripts/test-gemini-refresh.js index bc8f3cb0..2580adce 100644 --- a/scripts/test-gemini-refresh.js +++ b/scripts/test-gemini-refresh.js @@ -13,11 +13,47 @@ dotenv.config({ path: path.join(__dirname, '..', '.env') }); const redis = require('../src/models/redis'); const geminiAccountService = require('../src/services/geminiAccountService'); const logger = require('../src/utils/logger'); +const crypto = require('crypto'); +const config = require('../config/config'); + +// 加密相关常量(与 geminiAccountService 保持一致) +const ALGORITHM = 'aes-256-cbc'; +const ENCRYPTION_SALT = 'gemini-account-salt'; // 注意:是 'gemini-account-salt' 不是其他值! + +// 生成加密密钥 +function generateEncryptionKey() { + return crypto.scryptSync(config.security.encryptionKey, ENCRYPTION_SALT, 32); +} + +// 解密函数(用于调试) +function debugDecrypt(text) { + if (!text) return { success: false, error: 'Empty text' }; + try { + const key = generateEncryptionKey(); + const ivHex = text.substring(0, 32); + const encryptedHex = text.substring(33); + + const iv = Buffer.from(ivHex, 'hex'); + const encryptedText = Buffer.from(encryptedHex, 'hex'); + const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); + let decrypted = decipher.update(encryptedText); + decrypted = Buffer.concat([decrypted, decipher.final()]); + return { success: true, value: decrypted.toString() }; + } catch (error) { + return { success: false, error: error.message }; + } +} async function testGeminiTokenRefresh() { try { console.log('🚀 开始测试 Gemini token 刷新功能...\n'); + // 显示配置信息 + console.log('📋 配置信息:'); + console.log(` 加密密钥: ${config.security.encryptionKey}`); + console.log(` 加密盐值: ${ENCRYPTION_SALT}`); + console.log(); + // 1. 连接 Redis console.log('📡 连接 Redis...'); await redis.connect(); @@ -41,6 +77,26 @@ async function testGeminiTokenRefresh() { console.log(` 状态: ${account.status}`); try { + // 获取原始账户数据(用于调试) + const client = redis.getClient(); + const rawData = await client.hgetall(`gemini_account:${account.id}`); + + console.log(' 📊 原始数据检查:'); + console.log(` refreshToken 存在: ${rawData.refreshToken ? '是' : '否'}`); + if (rawData.refreshToken) { + console.log(` refreshToken 长度: ${rawData.refreshToken.length}`); + console.log(` refreshToken 前50字符: ${rawData.refreshToken.substring(0, 50)}...`); + + // 尝试手动解密 + const decryptResult = debugDecrypt(rawData.refreshToken); + if (decryptResult.success) { + console.log(` ✅ 手动解密成功`); + console.log(` 解密后前20字符: ${decryptResult.value.substring(0, 20)}...`); + } else { + console.log(` ❌ 手动解密失败: ${decryptResult.error}`); + } + } + // 获取完整账户信息(包括解密的 token) const fullAccount = await geminiAccountService.getAccount(account.id); @@ -49,7 +105,8 @@ async function testGeminiTokenRefresh() { continue; } - console.log(` ✅ 找到 refresh token`) + console.log(` ✅ 找到 refresh token`); + console.log(` 📝 解密后的 refresh token 前20字符: ${fullAccount.refreshToken.substring(0, 20)}...`); console.log(' 🔄 开始刷新 token...'); const startTime = Date.now();