跳至主要內容
進階

密鑰與身份

深入了解 Nostr 的密鑰系統,包括密鑰生成、編碼格式、安全管理和身份驗證。

18 分鐘

密鑰即身份

在 Nostr 中,你的身份完全由密鑰對定義。沒有用戶名、密碼或帳號系統。 這與比特幣的設計理念一致,「Not your keys, not your coins」 變成了「Not your keys, not your identity」。

與比特幣的關係: Nostr 使用與比特幣 Taproot 相同的 secp256k1 曲線和 Schnorr 簽名(BIP-340)。 這意味著你可以用同一個私鑰同時控制比特幣和 Nostr 身份。

密鑰對基礎

密鑰對組成:

私鑰 (Private Key / Secret Key)
├── 32 位元組(256 位元)隨機數
├── 必須絕對保密
├── 用於簽署事件
└── 十六進制格式或 nsec 編碼

公鑰 (Public Key)
├── 從私鑰導出
├── 32 位元組 x-only 公鑰(BIP-340)
├── 公開分享,作為身份標識
└── 十六進制格式或 npub 編碼

關係:
私鑰 ──(橢圓曲線乘法)──> 公鑰
      ← 不可逆 ←

NIP-19 編碼格式

NIP-19 定義了 bech32 編碼的人類可讀格式:

前綴 含義 內容
npub 公鑰 32 位元組公鑰
nsec 私鑰 32 位元組私鑰(敏感!)
note 事件 ID 32 位元組事件 ID
nprofile 用戶資料 公鑰 + 推薦中繼器
nevent 事件引用 事件 ID + 中繼器 + 作者
naddr 可尋址事件 kind + pubkey + d-tag + 中繼器
範例:

公鑰(十六進制):
3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d

npub 格式:
npub180cv... (以 npub1 開頭)

nprofile(包含中繼器提示):
nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl...

TLV 結構(nprofile, nevent, naddr):
┌────────┬────────┬─────────────┐
│ Type   │ Length │ Value       │
├────────┼────────┼─────────────┤
│ 0x00   │ 32     │ pubkey      │
│ 0x01   │ var    │ relay URL   │
│ ...    │ ...    │ ...         │
└────────┴────────┴─────────────┘

密鑰生成

密鑰生成方法:

1. 隨機生成
   - 使用密碼學安全的隨機數生成器(CSPRNG)
   - 生成 32 位元組隨機數作為私鑰
   - 計算對應公鑰

2. 從助記詞(BIP-39)
   - 使用 12 或 24 個單詞
   - 通過 BIP-32 派生路徑
   - 推薦路徑: m/44'/1237'/<account>'/0/0

JavaScript 範例(使用 nostr-tools):
import { generateSecretKey, getPublicKey } from 'nostr-tools'

// 隨機生成
const sk = generateSecretKey()  // Uint8Array
const pk = getPublicKey(sk)     // hex string

// 編碼為 bech32
import { nip19 } from 'nostr-tools'
const nsec = nip19.nsecEncode(sk)
const npub = nip19.npubEncode(pk)

安全警告: 永遠不要在不安全的環境(如瀏覽器控制台)生成密鑰。 使用可信的客戶端或離線工具生成密鑰。

簽名與驗證

Schnorr 簽名(BIP-340):

簽名過程:
1. 計算事件 ID(32 位元組哈希)
2. 使用私鑰對 ID 簽名
3. 產生 64 位元組簽名

sig = schnorr_sign(event_id, private_key)

驗證過程:
1. 重新計算事件 ID
2. 使用公鑰驗證簽名
3. 確認簽名有效

valid = schnorr_verify(event_id, sig, pubkey)

JavaScript 範例:
import { finalizeEvent, verifyEvent } from 'nostr-tools'

// 簽名事件
const signedEvent = finalizeEvent({
  kind: 1,
  created_at: Math.floor(Date.now() / 1000),
  tags: [],
  content: 'Hello Nostr!'
}, sk)

// 驗證事件
const isValid = verifyEvent(signedEvent)

密鑰管理

儲存方式

安全方式

  • • 硬體錢包(Ledger, Trezor)
  • • 密碼管理器(1Password, Bitwarden)
  • • 離線紙本備份
  • • 加密的本地儲存

危險方式

  • • 純文字檔案
  • • 雲端筆記(未加密)
  • • 螢幕截圖
  • • 剪貼簿(長期保留)

NIP-07 瀏覽器擴展

NIP-07 定義了瀏覽器擴展的標準介面:

window.nostr = {
  // 取得公鑰
  getPublicKey(): Promise<string>

  // 簽名事件(不暴露私鑰)
  signEvent(event): Promise<SignedEvent>

  // 加密訊息(NIP-04)
  nip04.encrypt(pubkey, plaintext): Promise<string>
  nip04.decrypt(pubkey, ciphertext): Promise<string>

  // 加密訊息(NIP-44,更安全)
  nip44.encrypt(pubkey, plaintext): Promise<string>
  nip44.decrypt(pubkey, ciphertext): Promise<string>
}

常見擴展:
- nos2x
- Alby
- Flamingo
- nostr-keyx

使用範例:
const pubkey = await window.nostr.getPublicKey()
const signedEvent = await window.nostr.signEvent(unsignedEvent)

NIP-46 遠端簽名

NIP-46(Nostr Connect)允許將簽名委託給遠端服務,私鑰不需要存在客戶端:

NIP-46 流程:

┌─────────────┐         ┌─────────────┐
│   客戶端    │◄───────►│  簽名器     │
│  (無私鑰)   │   NIP-46 │ (持有私鑰) │
└─────────────┘         └─────────────┘

1. 客戶端生成臨時密鑰對
2. 通過中繼器與簽名器建立連接
3. 客戶端發送簽名請求
4. 簽名器簽名並回傳

連接字串格式:
bunker://<signer-pubkey>?relay=<relay-url>&secret=<secret>

支援的操作:
- connect: 建立連接
- sign_event: 簽名事件
- get_public_key: 取得公鑰
- nip04_encrypt/decrypt: 加密訊息
- nip44_encrypt/decrypt: 加密訊息

簽名器服務:
- nsecBunker
- Amber (Android)

密鑰輪換與恢復

密鑰輪換的挑戰:

問題:
- Nostr 身份與公鑰綁定
- 換密鑰 = 新身份
- 關注者、歷史貼文都會丟失

緩解方案:

1. 公告遷移
   - 用舊密鑰發布遷移公告
   - 引導關注者追蹤新帳號
   - 新帳號發布接受公告

2. NIP-05 驗證
   - 保持域名驗證不變
   - 更新域名對應的公鑰
   - 用戶可通過域名找到新帳號

3. 多簽/社交恢復(研究中)
   - 通過信任的朋友恢復
   - 類似以太坊的社交恢復錢包

預防措施:
- 妥善備份私鑰
- 使用硬體錢包
- 考慮多重身份策略

NIP-05 域名身份

NIP-05 將人類可讀的域名映射到公鑰:

格式: [email protected]

驗證過程:
1. 客戶端查詢:
   GET https://domain.com/.well-known/nostr.json?name=user

2. 回應格式:
   {
     "names": {
       "user": "公鑰十六進制"
     },
     "relays": {
       "公鑰": ["wss://relay1.com", "wss://relay2.com"]
     }
   }

3. 客戶端驗證公鑰是否匹配

優點:
- 人類可讀的身份
- 可以更換密鑰(更新 JSON)
- 證明域名所有權
- 額外的信任指標

設定範例:
# 在你的域名下創建 /.well-known/nostr.json
{
  "names": {
    "alice": "abc123...",
    "_": "def456..."  // 根域名用戶 @domain.com
  }
}

安全最佳實踐

1. 使用專用身份

不要用控制大量比特幣的密鑰作為 Nostr 身份。 即使技術上可行,風險也太高。

2. 備份私鑰

使用多種方式備份:紙本、密碼管理器、加密 USB。 沒有「忘記密碼」功能可以恢復。

3. 使用 NIP-07 擴展

避免在網頁中直接輸入 nsec。 使用瀏覽器擴展,私鑰永不離開擴展。

4. 考慮 NIP-46

對於重要身份,使用遠端簽名器。 私鑰可以存放在更安全的環境。

5. 設定 NIP-05

即使密鑰洩漏需要更換, NIP-05 可以幫助用戶找到你的新身份。

下一步: 了解 中繼器運作 如何儲存和轉發你的事件。

已複製連結
已複製到剪貼簿