Files
nofx/scripts/migrate_encryption.go
ZhouYongyou 8c575a0613 feat(security): add end-to-end encryption for sensitive data
## Summary
Add comprehensive encryption system to protect private keys and API secrets.

## Core Components
- `crypto/encryption.go`: RSA-4096 + AES-256-GCM encryption manager
- `crypto/secure_storage.go`: Database encryption layer + audit logs
- `crypto/aliyun_kms.go`: Optional Aliyun KMS integration
- `api/crypto_handler.go`: Encryption API endpoints
- `web/src/lib/crypto.ts`: Frontend two-stage encryption
- `scripts/migrate_encryption.go`: Data migration tool
- `deploy_encryption.sh`: One-click deployment

## Security Architecture
```
Frontend: Two-stage input + clipboard obfuscation
    ↓
Transport: RSA-4096 + AES-256-GCM hybrid encryption
    ↓
Storage: Database encryption + audit logs
```

## Features
 Zero breaking changes (backward compatible)
 Automatic migration of existing data
 <25ms overhead per operation
 Complete audit trail
 Optional cloud KMS support

## Migration
```bash
./deploy_encryption.sh  # 5 minutes, zero downtime
```

## Testing
```bash
go test ./crypto -v
```

Related-To: security-enhancement
2025-11-06 23:55:33 +08:00

201 lines
4.7 KiB
Go

package main
import (
"database/sql"
"fmt"
"log"
"os"
"nofx/crypto"
_ "github.com/mattn/go-sqlite3"
)
func main() {
log.Println("🔄 開始遷移數據庫到加密格式...")
// 1. 檢查數據庫檔案
dbPath := "config.db"
if len(os.Args) > 1 {
dbPath = os.Args[1]
}
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
log.Fatalf("❌ 數據庫檔案不存在: %s", dbPath)
}
// 2. 備份數據庫
backupPath := fmt.Sprintf("%s.pre_encryption_backup", dbPath)
log.Printf("📦 備份數據庫到: %s", backupPath)
input, err := os.ReadFile(dbPath)
if err != nil {
log.Fatalf("❌ 讀取數據庫失敗: %v", err)
}
if err := os.WriteFile(backupPath, input, 0600); err != nil {
log.Fatalf("❌ 備份失敗: %v", err)
}
// 3. 打開數據庫
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
log.Fatalf("❌ 打開數據庫失敗: %v", err)
}
defer db.Close()
// 4. 初始化加密管理器
em, err := crypto.GetEncryptionManager()
if err != nil {
log.Fatalf("❌ 初始化加密管理器失敗: %v", err)
}
// 5. 遷移交易所配置
if err := migrateExchanges(db, em); err != nil {
log.Fatalf("❌ 遷移交易所配置失敗: %v", err)
}
// 6. 遷移 AI 模型配置
if err := migrateAIModels(db, em); err != nil {
log.Fatalf("❌ 遷移 AI 模型配置失敗: %v", err)
}
log.Println("✅ 數據遷移完成!")
log.Printf("📝 原始數據備份位於: %s", backupPath)
log.Println("⚠️ 請驗證系統功能正常後,手動刪除備份檔案")
}
// migrateExchanges 遷移交易所配置
func migrateExchanges(db *sql.DB, em *crypto.EncryptionManager) error {
log.Println("🔄 遷移交易所配置...")
// 查詢所有未加密的記錄(假設加密數據都包含 '==' Base64 特徵)
rows, err := db.Query(`
SELECT user_id, id, api_key, secret_key,
COALESCE(hyperliquid_private_key, ''),
COALESCE(aster_private_key, '')
FROM exchanges
WHERE (api_key != '' AND api_key NOT LIKE '%==%')
OR (secret_key != '' AND secret_key NOT LIKE '%==%')
`)
if err != nil {
return err
}
defer rows.Close()
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
count := 0
for rows.Next() {
var userID, exchangeID, apiKey, secretKey, hlPrivateKey, asterPrivateKey string
if err := rows.Scan(&userID, &exchangeID, &apiKey, &secretKey, &hlPrivateKey, &asterPrivateKey); err != nil {
return err
}
// 加密每個字段
encAPIKey, err := em.EncryptForDatabase(apiKey)
if err != nil {
return fmt.Errorf("加密 API Key 失敗: %w", err)
}
encSecretKey, err := em.EncryptForDatabase(secretKey)
if err != nil {
return fmt.Errorf("加密 Secret Key 失敗: %w", err)
}
encHLPrivateKey := ""
if hlPrivateKey != "" {
encHLPrivateKey, err = em.EncryptForDatabase(hlPrivateKey)
if err != nil {
return fmt.Errorf("加密 Hyperliquid Private Key 失敗: %w", err)
}
}
encAsterPrivateKey := ""
if asterPrivateKey != "" {
encAsterPrivateKey, err = em.EncryptForDatabase(asterPrivateKey)
if err != nil {
return fmt.Errorf("加密 Aster Private Key 失敗: %w", err)
}
}
// 更新數據庫
_, err = tx.Exec(`
UPDATE exchanges
SET api_key = ?, secret_key = ?,
hyperliquid_private_key = ?, aster_private_key = ?
WHERE user_id = ? AND id = ?
`, encAPIKey, encSecretKey, encHLPrivateKey, encAsterPrivateKey, userID, exchangeID)
if err != nil {
return fmt.Errorf("更新數據庫失敗: %w", err)
}
log.Printf(" ✓ 已加密: [%s] %s", userID, exchangeID)
count++
}
if err := tx.Commit(); err != nil {
return err
}
log.Printf("✅ 已遷移 %d 個交易所配置", count)
return nil
}
// migrateAIModels 遷移 AI 模型配置
func migrateAIModels(db *sql.DB, em *crypto.EncryptionManager) error {
log.Println("🔄 遷移 AI 模型配置...")
rows, err := db.Query(`
SELECT user_id, id, api_key
FROM ai_models
WHERE api_key != '' AND api_key NOT LIKE '%==%'
`)
if err != nil {
return err
}
defer rows.Close()
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
count := 0
for rows.Next() {
var userID, modelID, apiKey string
if err := rows.Scan(&userID, &modelID, &apiKey); err != nil {
return err
}
encAPIKey, err := em.EncryptForDatabase(apiKey)
if err != nil {
return fmt.Errorf("加密 API Key 失敗: %w", err)
}
_, err = tx.Exec(`
UPDATE ai_models SET api_key = ? WHERE user_id = ? AND id = ?
`, encAPIKey, userID, modelID)
if err != nil {
return fmt.Errorf("更新數據庫失敗: %w", err)
}
log.Printf(" ✓ 已加密: [%s] %s", userID, modelID)
count++
}
if err := tx.Commit(); err != nil {
return err
}
log.Printf("✅ 已遷移 %d 個 AI 模型配置", count)
return nil
}