跳至主要內容
進階

Wallet Encryption

深入了解 Bitcoin Core 錢包加密機制,如何保護私鑰安全。

10 分鐘

為什麼需要錢包加密?

Bitcoin Core 錢包存儲私鑰,這些私鑰控制你的比特幣。如果攻擊者獲得了 wallet.dat 文件的副本, 他們可以竊取所有資金。錢包加密使用密碼保護私鑰,即使文件被盜也無法直接使用。

加密保護的內容

✓ 加密保護

  • • 私鑰(Private Keys)
  • • HD 種子(Master Seed)
  • • 導入的密鑰

⚠ 未加密

  • • 公鑰和地址
  • • 交易歷史
  • • 標籤和元數據

加密方案

AES-256-CBC

Bitcoin Core 使用 AES-256-CBC 加密私鑰。加密密鑰通過 PBKDF2 從用戶密碼派生:

加密流程:

用戶密碼
    ↓
PBKDF2-HMAC-SHA512 (25000 迭代)
    ↓
主密鑰 (Master Key, 32 bytes)
    ↓
AES-256-CBC 加密
    ↓
加密的私鑰

存儲結構:
┌─────────────────────────────────┐
│ Salt (8 bytes)                  │
├─────────────────────────────────┤
│ Encrypted Master Key            │
├─────────────────────────────────┤
│ IV (16 bytes per key)           │
├─────────────────────────────────┤
│ Encrypted Private Key           │
└─────────────────────────────────┘

密鑰派生

import { pbkdf2Sync, createCipheriv, createDecipheriv, randomBytes } from 'crypto';

interface EncryptedWallet {
  salt: Buffer;
  encryptedMasterKey: Buffer;
  masterKeyIV: Buffer;
  encryptedKeys: Map;
}

interface EncryptedKey {
  iv: Buffer;
  ciphertext: Buffer;
}

// 從密碼派生加密密鑰
function deriveKey(password: string, salt: Buffer): Buffer {
  return pbkdf2Sync(
    password,
    salt,
    25000,        // 迭代次數
    32,           // 密鑰長度 (256 bits)
    'sha512'
  );
}

// 加密私鑰
function encryptPrivateKey(
  privateKey: Buffer,
  masterKey: Buffer
): EncryptedKey {
  const iv = randomBytes(16);
  const cipher = createCipheriv('aes-256-cbc', masterKey, iv);

  const ciphertext = Buffer.concat([
    cipher.update(privateKey),
    cipher.final()
  ]);

  return { iv, ciphertext };
}

// 解密私鑰
function decryptPrivateKey(
  encrypted: EncryptedKey,
  masterKey: Buffer
): Buffer {
  const decipher = createDecipheriv(
    'aes-256-cbc',
    masterKey,
    encrypted.iv
  );

  return Buffer.concat([
    decipher.update(encrypted.ciphertext),
    decipher.final()
  ]);
}

// 完整的錢包加密流程
function encryptWallet(
  privateKeys: Map,
  password: string
): EncryptedWallet {
  // 1. 生成隨機 salt
  const salt = randomBytes(8);

  // 2. 派生密鑰加密密鑰 (KEK)
  const kek = deriveKey(password, salt);

  // 3. 生成隨機主密鑰
  const masterKey = randomBytes(32);

  // 4. 用 KEK 加密主密鑰
  const masterKeyIV = randomBytes(16);
  const cipher = createCipheriv('aes-256-cbc', kek, masterKeyIV);
  const encryptedMasterKey = Buffer.concat([
    cipher.update(masterKey),
    cipher.final()
  ]);

  // 5. 用主密鑰加密所有私鑰
  const encryptedKeys = new Map();
  for (const [address, privateKey] of privateKeys) {
    encryptedKeys.set(address, encryptPrivateKey(privateKey, masterKey));
  }

  return {
    salt,
    encryptedMasterKey,
    masterKeyIV,
    encryptedKeys
  };
}

使用方式

加密錢包

# 加密現有錢包(只能執行一次)
bitcoin-cli encryptwallet "your-strong-passphrase"

# 注意:這會關閉 Bitcoin Core
# 重新啟動後錢包將被加密

# 加密後,錢包會自動鎖定
# 需要解鎖才能進行需要私鑰的操作

警告: 加密錢包後無法取消加密。如果忘記密碼,將永久失去對資金的訪問權。 在加密前,請確保備份 wallet.dat 和記住密碼。

解鎖錢包

# 解鎖錢包 60 秒
bitcoin-cli walletpassphrase "your-passphrase" 60

# 解鎖錢包用於 staking(不允許發送)
bitcoin-cli walletpassphrase "your-passphrase" 60 true

# 手動鎖定錢包
bitcoin-cli walletlock

# 查看錢包狀態
bitcoin-cli getwalletinfo | jq '.unlocked_until'
# 0 = 已鎖定
# > 0 = 解鎖到的 Unix 時間戳

更改密碼

# 更改錢包密碼
bitcoin-cli walletpassphrasechange "old-passphrase" "new-passphrase"

# 這會重新加密主密鑰
# 不需要重新加密所有私鑰

安全性考量

密碼強度

暴力破解估算

密碼類型 破解時間*
6 個小寫字母 ~28 bits
8 個混合字符 ~47 bits
12 個混合字符 ~71 bits 數十年
4 個隨機單詞 ~44 bits
6 個隨機單詞 ~66 bits 數千年

* 假設攻擊者使用高端 GPU,考慮 PBKDF2 的 25000 次迭代

攻擊向量

潛在威脅

  • 1.
    內存攻擊: 解鎖時,私鑰會暫時存在於內存中。惡意軟體可能讀取內存。
  • 2.
    鍵盤記錄: 惡意軟體可能記錄密碼輸入。
  • 3.
    文件備份: 加密前的備份可能未加密。
  • 4.
    弱密碼: 簡單密碼可被暴力破解。

內存安全

安全內存處理

Bitcoin Core 採取措施保護內存中的敏感數據:

  • mlock(): 防止敏感數據被交換到磁碟(swap)
  • 清零內存: 使用完私鑰後,將內存清零
  • 自動鎖定: 超時後自動鎖定錢包
// Bitcoin Core 的安全內存分配 (簡化)
class secure_allocator {
public:
    void* allocate(size_t n) {
        void* ptr = std::malloc(n);
        if (ptr) {
            // 防止交換到磁碟
            mlock(ptr, n);
        }
        return ptr;
    }

    void deallocate(void* ptr, size_t n) {
        if (ptr) {
            // 使用前清零
            memory_cleanse(ptr, n);
            munlock(ptr, n);
            std::free(ptr);
        }
    }
};

// 安全清零(防止編譯器優化掉)
void memory_cleanse(void* ptr, size_t len) {
    volatile unsigned char* p = (volatile unsigned char*)ptr;
    while (len--) {
        *p++ = 0;
    }
}

Descriptor 錢包

新錢包格式

Bitcoin Core 23.0+ 預設使用 Descriptor 錢包,它使用 SQLite 存儲並有不同的加密方式:

# 創建加密的 descriptor 錢包
bitcoin-cli createwallet "mywallet" false false "passphrase"

# 參數說明:
# - 錢包名稱
# - disable_private_keys (false = 包含私鑰)
# - blank (false = 創建預設密鑰)
# - passphrase (加密密碼)

# 查看錢包類型
bitcoin-cli getwalletinfo | jq '.format'
# "sqlite" = descriptor 錢包
# "bdb" = 傳統錢包

最佳實踐

✓ 推薦做法

  • • 使用強密碼(12+ 字符或 6+ 隨機單詞)
  • • 設定短的解鎖超時時間
  • • 定期備份加密的錢包
  • • 使用密碼管理器存儲密碼
  • • 保持系統更新和安全

✗ 避免

  • • 使用弱密碼或常見密碼
  • • 長時間保持錢包解鎖
  • • 在不安全的系統上解鎖
  • • 忘記密碼(無法恢復!)
  • • 將密碼與錢包備份放在一起

備份策略

# 備份加密的錢包
bitcoin-cli backupwallet "/path/to/backup/wallet.dat"

# 對於 descriptor 錢包
bitcoin-cli -rpcwallet=mywallet backupwallet "/path/to/backup/mywallet.sqlite"

# 導出私鑰(需要解鎖)
bitcoin-cli walletpassphrase "passphrase" 60
bitcoin-cli dumpprivkey "bc1q..."

# 導出所有描述符(descriptor 錢包)
bitcoin-cli listdescriptors true  # true = 包含私鑰

備份建議: 備份應該包含加密的錢包文件和(分開存儲的)密碼。 考慮使用多個備份位置,包括離線存儲。

總結

  • AES-256-CBC:使用強加密保護私鑰
  • PBKDF2:25,000 次迭代減慢暴力破解
  • 密碼強度:使用強密碼是安全的關鍵
  • 不可逆:忘記密碼意味著永久失去資金
已複製連結
已複製到剪貼簿