package crypto import ( "database/sql" "fmt" "log" "time" ) // SecureStorage 安全存儲層(自動加密/解密數據庫中的敏感字段) type SecureStorage struct { db *sql.DB em *EncryptionManager } // NewSecureStorage 創建安全存儲實例 func NewSecureStorage(db *sql.DB) (*SecureStorage, error) { em, err := GetEncryptionManager() if err != nil { return nil, err } ss := &SecureStorage{ db: db, em: em, } // 初始化審計日誌表 if err := ss.initAuditLog(); err != nil { return nil, fmt.Errorf("初始化審計日誌失敗: %w", err) } return ss, nil } // ==================== 交易所配置加密存儲 ==================== // SaveEncryptedExchangeConfig 保存加密的交易所配置 func (ss *SecureStorage) SaveEncryptedExchangeConfig(userID, exchangeID, apiKey, secretKey, asterPrivateKey string) error { // 加密敏感字段 encryptedAPIKey, err := ss.em.EncryptForDatabase(apiKey) if err != nil { return fmt.Errorf("加密 API Key 失敗: %w", err) } encryptedSecretKey, err := ss.em.EncryptForDatabase(secretKey) if err != nil { return fmt.Errorf("加密 Secret Key 失敗: %w", err) } encryptedPrivateKey := "" if asterPrivateKey != "" { encryptedPrivateKey, err = ss.em.EncryptForDatabase(asterPrivateKey) if err != nil { return fmt.Errorf("加密 Private Key 失敗: %w", err) } } // 更新數據庫 _, err = ss.db.Exec(` UPDATE exchanges SET api_key = ?, secret_key = ?, aster_private_key = ?, updated_at = datetime('now') WHERE user_id = ? AND id = ? `, encryptedAPIKey, encryptedSecretKey, encryptedPrivateKey, userID, exchangeID) if err != nil { return err } // 記錄審計日誌 ss.logAudit(userID, "exchange_config_update", exchangeID, "密鑰已更新") log.Printf("🔐 [%s] 交易所 %s 的密鑰已加密保存", userID, exchangeID) return nil } // LoadDecryptedExchangeConfig 加載並解密交易所配置 func (ss *SecureStorage) LoadDecryptedExchangeConfig(userID, exchangeID string) (apiKey, secretKey, asterPrivateKey string, err error) { var encryptedAPIKey, encryptedSecretKey, encryptedPrivateKey sql.NullString err = ss.db.QueryRow(` SELECT api_key, secret_key, aster_private_key FROM exchanges WHERE user_id = ? AND id = ? `, userID, exchangeID).Scan(&encryptedAPIKey, &encryptedSecretKey, &encryptedPrivateKey) if err != nil { return "", "", "", err } // 解密 API Key if encryptedAPIKey.Valid && encryptedAPIKey.String != "" { apiKey, err = ss.em.DecryptFromDatabase(encryptedAPIKey.String) if err != nil { return "", "", "", fmt.Errorf("解密 API Key 失敗: %w", err) } } // 解密 Secret Key if encryptedSecretKey.Valid && encryptedSecretKey.String != "" { secretKey, err = ss.em.DecryptFromDatabase(encryptedSecretKey.String) if err != nil { return "", "", "", fmt.Errorf("解密 Secret Key 失敗: %w", err) } } // 解密 Private Key if encryptedPrivateKey.Valid && encryptedPrivateKey.String != "" { asterPrivateKey, err = ss.em.DecryptFromDatabase(encryptedPrivateKey.String) if err != nil { return "", "", "", fmt.Errorf("解密 Private Key 失敗: %w", err) } } // 記錄審計日誌 ss.logAudit(userID, "exchange_config_read", exchangeID, "密鑰已讀取") return apiKey, secretKey, asterPrivateKey, nil } // ==================== AI 模型配置加密存儲 ==================== // SaveEncryptedAIModelConfig 保存加密的 AI 模型 API Key func (ss *SecureStorage) SaveEncryptedAIModelConfig(userID, modelID, apiKey string) error { encryptedAPIKey, err := ss.em.EncryptForDatabase(apiKey) if err != nil { return fmt.Errorf("加密 API Key 失敗: %w", err) } _, err = ss.db.Exec(` UPDATE ai_models SET api_key = ?, updated_at = datetime('now') WHERE user_id = ? AND id = ? `, encryptedAPIKey, userID, modelID) if err != nil { return err } ss.logAudit(userID, "ai_model_config_update", modelID, "API Key 已更新") log.Printf("🔐 [%s] AI 模型 %s 的 API Key 已加密保存", userID, modelID) return nil } // LoadDecryptedAIModelConfig 加載並解密 AI 模型配置 func (ss *SecureStorage) LoadDecryptedAIModelConfig(userID, modelID string) (string, error) { var encryptedAPIKey sql.NullString err := ss.db.QueryRow(` SELECT api_key FROM ai_models WHERE user_id = ? AND id = ? `, userID, modelID).Scan(&encryptedAPIKey) if err != nil { return "", err } if !encryptedAPIKey.Valid || encryptedAPIKey.String == "" { return "", nil } apiKey, err := ss.em.DecryptFromDatabase(encryptedAPIKey.String) if err != nil { return "", fmt.Errorf("解密 API Key 失敗: %w", err) } ss.logAudit(userID, "ai_model_config_read", modelID, "API Key 已讀取") return apiKey, nil } // ==================== 審計日誌 ==================== // initAuditLog 初始化審計日誌表 func (ss *SecureStorage) initAuditLog() error { _, err := ss.db.Exec(` CREATE TABLE IF NOT EXISTS audit_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, action TEXT NOT NULL, resource TEXT NOT NULL, details TEXT, ip_address TEXT, user_agent TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_time (user_id, timestamp), INDEX idx_action (action) ) `) return err } // logAudit 記錄審計日誌 func (ss *SecureStorage) logAudit(userID, action, resource, details string) { _, err := ss.db.Exec(` INSERT INTO audit_logs (user_id, action, resource, details) VALUES (?, ?, ?, ?) `, userID, action, resource, details) if err != nil { log.Printf("⚠️ 審計日誌記錄失敗: %v", err) } } // GetAuditLogs 查詢審計日誌 func (ss *SecureStorage) GetAuditLogs(userID string, limit int) ([]AuditLog, error) { rows, err := ss.db.Query(` SELECT id, user_id, action, resource, details, timestamp FROM audit_logs WHERE user_id = ? ORDER BY timestamp DESC LIMIT ? `, userID, limit) if err != nil { return nil, err } defer rows.Close() var logs []AuditLog for rows.Next() { var log AuditLog err := rows.Scan(&log.ID, &log.UserID, &log.Action, &log.Resource, &log.Details, &log.Timestamp) if err != nil { return nil, err } logs = append(logs, log) } return logs, nil } // AuditLog 審計日誌結構 type AuditLog struct { ID int64 `json:"id"` UserID string `json:"user_id"` Action string `json:"action"` Resource string `json:"resource"` Details string `json:"details"` Timestamp time.Time `json:"timestamp"` } // ==================== 數據遷移工具 ==================== // MigrateToEncrypted 將舊的明文數據遷移到加密格式 func (ss *SecureStorage) MigrateToEncrypted() error { log.Println("🔄 開始遷移明文數據到加密格式...") tx, err := ss.db.Begin() if err != nil { return err } defer tx.Rollback() // 遷移交易所配置 rows, err := tx.Query(` SELECT user_id, id, api_key, secret_key, aster_private_key FROM exchanges WHERE api_key != '' AND api_key NOT LIKE '%==%' -- 過濾已加密數據 `) if err != nil { return err } var count int for rows.Next() { var userID, exchangeID, apiKey, secretKey string var asterPrivateKey sql.NullString if err := rows.Scan(&userID, &exchangeID, &apiKey, &secretKey, &asterPrivateKey); err != nil { rows.Close() return err } // 加密 encAPIKey, _ := ss.em.EncryptForDatabase(apiKey) encSecretKey, _ := ss.em.EncryptForDatabase(secretKey) encPrivateKey := "" if asterPrivateKey.Valid && asterPrivateKey.String != "" { encPrivateKey, _ = ss.em.EncryptForDatabase(asterPrivateKey.String) } // 更新 _, err = tx.Exec(` UPDATE exchanges SET api_key = ?, secret_key = ?, aster_private_key = ? WHERE user_id = ? AND id = ? `, encAPIKey, encSecretKey, encPrivateKey, userID, exchangeID) if err != nil { rows.Close() return err } count++ } rows.Close() if err := tx.Commit(); err != nil { return err } log.Printf("✅ 已遷移 %d 個交易所配置到加密格式", count) return nil }