進階
密鑰與身份
深入了解 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 可以幫助用戶找到你的新身份。
下一步: 了解 中繼器運作 如何儲存和轉發你的事件。
已複製連結