mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
refactor(crypto): simplify to local encryption only (remove KMS)
## 🎯 簡化方案(社區友好) ### 移除雲端 KMS - ❌ 刪除 crypto/aliyun_kms.go - ❌ 不包含 GCP KMS - ✅ 僅保留本地 AES-256-GCM 加密 ### 更新 SQLite 驅動 - ✅ modernc.org/sqlite(純 Go,無 CGO) - ✅ 與上游保持一致 ## 📦 保留核心功能 ✅ crypto/encryption.go - RSA + AES 加密 ✅ crypto/secure_storage.go - 數據庫加密層 ✅ api/crypto_handler.go - API 端點 ✅ web/src/lib/crypto.ts - 前端加密 ✅ scripts/migrate_encryption.go - 數據遷移 ## 🚀 部署方式 ```bash # 僅需一個環境變量 export NOFX_MASTER_KEY=$(openssl rand -base64 32) go run main.go ``` ## ✅ 優點 - ✅ 零雲服務依賴 - ✅ 簡單易部署 - ✅ 適合社區用戶 - ✅ 保持核心安全功能
This commit is contained in:
@@ -1,216 +0,0 @@
|
|||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AliyunKMSManager 阿里雲 KMS 管理器
|
|
||||||
type AliyunKMSManager struct {
|
|
||||||
client *kms.Client
|
|
||||||
keyID string // 主密鑰 ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAliyunKMSManager 創建阿里雲 KMS 管理器
|
|
||||||
func NewAliyunKMSManager() (*AliyunKMSManager, error) {
|
|
||||||
// 從環境變數讀取配置
|
|
||||||
accessKeyID := os.Getenv("ALIYUN_ACCESS_KEY_ID")
|
|
||||||
accessKeySecret := os.Getenv("ALIYUN_ACCESS_KEY_SECRET")
|
|
||||||
regionID := os.Getenv("ALIYUN_REGION_ID") // 如 cn-hangzhou
|
|
||||||
keyID := os.Getenv("ALIYUN_KMS_KEY_ID") // 主密鑰 ID
|
|
||||||
|
|
||||||
if accessKeyID == "" || accessKeySecret == "" {
|
|
||||||
return nil, fmt.Errorf("阿里雲憑證未配置,請設置環境變數 ALIYUN_ACCESS_KEY_ID 和 ALIYUN_ACCESS_KEY_SECRET")
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyID == "" {
|
|
||||||
return nil, fmt.Errorf("KMS 密鑰 ID 未配置,請設置環境變數 ALIYUN_KMS_KEY_ID")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 創建 KMS 客戶端
|
|
||||||
client, err := kms.NewClientWithAccessKey(regionID, accessKeyID, accessKeySecret)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("創建 KMS 客戶端失敗: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &AliyunKMSManager{
|
|
||||||
client: client,
|
|
||||||
keyID: keyID,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt 使用 KMS 加密數據
|
|
||||||
func (m *AliyunKMSManager) Encrypt(plaintext string) (string, error) {
|
|
||||||
request := kms.CreateEncryptRequest()
|
|
||||||
request.Scheme = "https"
|
|
||||||
request.KeyId = m.keyID
|
|
||||||
request.Plaintext = plaintext
|
|
||||||
|
|
||||||
response, err := m.client.Encrypt(request)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("KMS 加密失敗: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.CiphertextBlob, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt 使用 KMS 解密數據
|
|
||||||
func (m *AliyunKMSManager) Decrypt(ciphertext string) (string, error) {
|
|
||||||
request := kms.CreateDecryptRequest()
|
|
||||||
request.Scheme = "https"
|
|
||||||
request.CiphertextBlob = ciphertext
|
|
||||||
|
|
||||||
response, err := m.client.Decrypt(request)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("KMS 解密失敗: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base64 解碼
|
|
||||||
plaintext, err := base64.StdEncoding.DecodeString(response.Plaintext)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("解碼失敗: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(plaintext), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateDataKey 生成數據密鑰(用於本地加密,KMS 僅管理主密鑰)
|
|
||||||
func (m *AliyunKMSManager) GenerateDataKey() (plaintext, ciphertext string, err error) {
|
|
||||||
request := kms.CreateGenerateDataKeyRequest()
|
|
||||||
request.Scheme = "https"
|
|
||||||
request.KeyId = m.keyID
|
|
||||||
request.KeySpec = "AES_256" // 256-bit AES 密鑰
|
|
||||||
|
|
||||||
response, err := m.client.GenerateDataKey(request)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("生成數據密鑰失敗: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 明文密鑰(用於加密數據)
|
|
||||||
plaintextBytes, _ := base64.StdEncoding.DecodeString(response.Plaintext)
|
|
||||||
plaintext = string(plaintextBytes)
|
|
||||||
|
|
||||||
// 密文密鑰(保存到數據庫,用於後續解密)
|
|
||||||
ciphertext = response.CiphertextBlob
|
|
||||||
|
|
||||||
return plaintext, ciphertext, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKey 創建新的 KMS 主密鑰(僅管理員操作)
|
|
||||||
func (m *AliyunKMSManager) CreateKey(description string) (string, error) {
|
|
||||||
request := kms.CreateCreateKeyRequest()
|
|
||||||
request.Scheme = "https"
|
|
||||||
request.Description = description
|
|
||||||
request.KeyUsage = "ENCRYPT/DECRYPT"
|
|
||||||
request.Origin = "Aliyun_KMS" // 阿里雲託管
|
|
||||||
|
|
||||||
response, err := m.client.CreateKey(request)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("創建 KMS 密鑰失敗: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.KeyMetadata.KeyId, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableKeyRotation 啟用自動密鑰輪換(每年自動輪換)
|
|
||||||
func (m *AliyunKMSManager) EnableKeyRotation() error {
|
|
||||||
request := kms.CreateEnableKeyRotationRequest()
|
|
||||||
request.Scheme = "https"
|
|
||||||
request.KeyId = m.keyID
|
|
||||||
|
|
||||||
_, err := m.client.EnableKeyRotation(request)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("啟用密鑰輪換失敗: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 與現有加密系統集成 ====================
|
|
||||||
|
|
||||||
// EncryptionManagerWithKMS 混合加密管理器(本地 + KMS)
|
|
||||||
type EncryptionManagerWithKMS struct {
|
|
||||||
localEM *EncryptionManager
|
|
||||||
kmsEM *AliyunKMSManager
|
|
||||||
useKMS bool // 是否使用 KMS
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncryptionManagerWithKMS 創建混合加密管理器
|
|
||||||
func NewEncryptionManagerWithKMS() (*EncryptionManagerWithKMS, error) {
|
|
||||||
// 初始化本地加密
|
|
||||||
localEM, err := GetEncryptionManager()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 嘗試初始化 KMS(如果配置了環境變數)
|
|
||||||
kmsEM, err := NewAliyunKMSManager()
|
|
||||||
useKMS := err == nil
|
|
||||||
|
|
||||||
if useKMS {
|
|
||||||
fmt.Println("✅ 阿里雲 KMS 已啟用")
|
|
||||||
} else {
|
|
||||||
fmt.Println("⚠️ 阿里雲 KMS 未配置,使用本地加密")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &EncryptionManagerWithKMS{
|
|
||||||
localEM: localEM,
|
|
||||||
kmsEM: kmsEM,
|
|
||||||
useKMS: useKMS,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptForDatabase 加密數據(自動選擇 KMS 或本地)
|
|
||||||
func (m *EncryptionManagerWithKMS) EncryptForDatabase(plaintext string) (string, error) {
|
|
||||||
if m.useKMS {
|
|
||||||
// 使用 KMS 加密
|
|
||||||
encrypted, err := m.kmsEM.Encrypt(plaintext)
|
|
||||||
if err != nil {
|
|
||||||
// KMS 失敗時降級到本地加密
|
|
||||||
fmt.Printf("⚠️ KMS 加密失敗,降級到本地加密: %v\n", err)
|
|
||||||
return m.localEM.EncryptForDatabase(plaintext)
|
|
||||||
}
|
|
||||||
return "kms:" + encrypted, nil // 添加前綴標識
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用本地加密
|
|
||||||
return m.localEM.EncryptForDatabase(plaintext)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptFromDatabase 解密數據(自動檢測 KMS 或本地)
|
|
||||||
func (m *EncryptionManagerWithKMS) DecryptFromDatabase(ciphertext string) (string, error) {
|
|
||||||
// 檢測是否為 KMS 加密
|
|
||||||
if len(ciphertext) > 4 && ciphertext[:4] == "kms:" {
|
|
||||||
if !m.useKMS {
|
|
||||||
return "", fmt.Errorf("數據使用 KMS 加密,但 KMS 未配置")
|
|
||||||
}
|
|
||||||
return m.kmsEM.Decrypt(ciphertext[4:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 本地解密
|
|
||||||
return m.localEM.DecryptFromDatabase(ciphertext)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MigrateToKMS 將現有本地加密數據遷移到 KMS
|
|
||||||
func (m *EncryptionManagerWithKMS) MigrateToKMS(localEncrypted string) (string, error) {
|
|
||||||
if !m.useKMS {
|
|
||||||
return "", fmt.Errorf("KMS 未啟用")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. 本地解密
|
|
||||||
plaintext, err := m.localEM.DecryptFromDatabase(localEncrypted)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. KMS 加密
|
|
||||||
kmsEncrypted, err := m.kmsEM.Encrypt(plaintext)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return "kms:" + kmsEncrypted, nil
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"nofx/crypto"
|
"nofx/crypto"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -38,7 +38,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 打開數據庫
|
// 3. 打開數據庫
|
||||||
db, err := sql.Open("sqlite3", dbPath)
|
db, err := sql.Open("sqlite", dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("❌ 打開數據庫失敗: %v", err)
|
log.Fatalf("❌ 打開數據庫失敗: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user