跳至主要內容
高級

NIP-46 遠端簽名

深入了解 Nostr Connect 遠端簽名協議,實現跨設備安全簽名。

12 分鐘

什麼是 NIP-46?

NIP-46(Nostr Connect)定義了一種遠端簽名協議,讓應用程式可以向遠端簽名器 (如手機 App、硬體設備)請求簽名,而私鑰永遠不會離開簽名器。 這類似於 WalletConnect 在以太坊生態系統中的作用。

安全優勢: 私鑰保存在你信任的設備上(如手機),網頁應用只能請求簽名, 無法存取或匯出你的私鑰。每個請求都需要你在簽名器上確認。

運作原理

1

建立連接

客戶端生成臨時密鑰對,通過 bunker:// URI 或 nostrconnect:// 與簽名器建立連接

2

加密通訊

雙方通過中繼器交換 NIP-04/NIP-44 加密的訊息

3

請求簽名

客戶端發送簽名請求(如 sign_event),簽名器顯示確認對話框

4

返回結果

用戶確認後,簽名器返回已簽名的事件或加密結果

連接 URI 格式

bunker:// URI

由簽名器提供,客戶端掃描或輸入來連接:

bunker://<remote-signer-pubkey>?relay=<relay-url>&secret=<secret>

# 範例
bunker://a]7f6...?relay=wss://relay.nsec.app&secret=abc123

# 參數說明
- remote-signer-pubkey: 簽名器的公鑰
- relay: 用於通訊的中繼器
- secret: 可選的連接密鑰

nostrconnect:// URI

由客戶端生成,簽名器掃描來連接:

nostrconnect://<client-pubkey>?relay=<relay>&metadata=<metadata>

# 範例
nostrconnect://b8e3...?relay=wss://relay.damus.io&metadata={
  "name": "My App",
  "url": "https://myapp.com",
  "description": "A cool Nostr app"
}

# 參數說明
- client-pubkey: 客戶端的臨時公鑰
- relay: 用於通訊的中繼器
- metadata: 應用程式資訊(JSON 編碼)

RPC 方法

方法 說明 參數
connect 建立連接 pubkey, secret?, perms?
get_public_key 取得公鑰 -
sign_event 簽署事件 event (JSON)
nip04_encrypt NIP-04 加密 pubkey, plaintext
nip04_decrypt NIP-04 解密 pubkey, ciphertext
nip44_encrypt NIP-44 加密 pubkey, plaintext
nip44_decrypt NIP-44 解密 pubkey, ciphertext
ping 心跳檢測 -

訊息格式

請求訊息

// 請求格式(加密前)
{
  "id": "<random-id>",
  "method": "sign_event",
  "params": ["<event-json>"]
}

// 作為 kind 24133 事件發送
{
  "kind": 24133,
  "pubkey": "<client-pubkey>",
  "content": "<nip04-encrypted-request>",
  "tags": [
    ["p", "<remote-signer-pubkey>"]
  ],
  ...
}

回應訊息

// 成功回應
{
  "id": "<request-id>",
  "result": "<signed-event-json>"
}

// 錯誤回應
{
  "id": "<request-id>",
  "error": "User rejected the request"
}

程式碼範例

使用 nostr-tools

import { Nip46Signer } from 'nostr-tools/nip46'

// 從 bunker:// URI 建立連接
const bunkerUri = 'bunker://a7f6...?relay=wss://relay.nsec.app'
const signer = new Nip46Signer(bunkerUri)

// 等待連接建立
await signer.connect()

// 取得公鑰
const pubkey = await signer.getPublicKey()
console.log('公鑰:', pubkey)

// 簽署事件
const unsignedEvent = {
  kind: 1,
  created_at: Math.floor(Date.now() / 1000),
  tags: [],
  content: 'Hello from Nostr Connect!'
}

const signedEvent = await signer.signEvent(unsignedEvent)
console.log('已簽名:', signedEvent)

// 加密訊息
const encrypted = await signer.nip04Encrypt(recipientPubkey, 'Secret message')

// 斷開連接
signer.close()

生成 nostrconnect:// URI

import { generateSecretKey, getPublicKey } from 'nostr-tools'

function generateConnectUri(relay, appMetadata) {
  // 生成客戶端臨時密鑰
  const clientSecret = generateSecretKey()
  const clientPubkey = getPublicKey(clientSecret)

  // 編碼 metadata
  const metadata = encodeURIComponent(JSON.stringify(appMetadata))

  // 構建 URI
  const uri = `nostrconnect://${clientPubkey}?relay=${encodeURIComponent(relay)}&metadata=${metadata}`

  return { uri, clientSecret, clientPubkey }
}

// 使用
const { uri } = generateConnectUri(
  'wss://relay.damus.io',
  {
    name: 'My Nostr App',
    url: 'https://myapp.com',
    description: 'A cool Nostr application'
  }
)

// 顯示 QR 碼讓簽名器掃描
displayQRCode(uri)

權限系統

簽名器可以要求客戶端聲明需要的權限:

// 權限字串格式
perms=sign_event:1,sign_event:4,nip04_encrypt,nip04_decrypt

// 常見權限
- sign_event:<kind>  // 簽署特定類型事件
- nip04_encrypt      // NIP-04 加密
- nip04_decrypt      // NIP-04 解密
- nip44_encrypt      // NIP-44 加密
- nip44_decrypt      // NIP-44 解密

// 範例:只允許發布貼文和私訊
perms=sign_event:1,sign_event:4,nip04_encrypt,nip04_decrypt

支援的簽名器

NIP-07 vs NIP-46

特性 NIP-07 NIP-46
簽名位置 同一設備(瀏覽器擴充) 遠端設備(手機、伺服器)
通訊方式 本地 JavaScript API 中繼器加密訊息
跨設備 不支援 支援
適用場景 桌面網頁應用 任何環境(含行動裝置)
延遲 低(本地) 較高(網路)

安全考量

優點

  • • 私鑰永不離開簽名器
  • • 每次請求可審核
  • • 支援細粒度權限控制
  • • 可跨設備使用

注意事項

  • • 依賴中繼器可用性
  • • 連接密鑰需妥善保管
  • • 網路延遲影響體驗
  • • 需要簽名器在線

常見使用場景

手機作為簽名器

在電腦上使用網頁應用,簽名請求發送到手機上的 Amber/Nostr Signer 確認。

團隊共享帳號

使用 nsecBunker 伺服器管理密鑰,多人可請求簽名而不需分享私鑰。

自動化服務

Bot 或服務可以請求人工審核後簽名,避免自動化洩露私鑰風險。

冷儲存簽名

私鑰存放在離線設備,通過 QR 碼傳遞簽名請求和結果。

開發建議: 應用程式應同時支援 NIP-07 和 NIP-46,讓用戶可以選擇使用瀏覽器擴充或遠端簽名器。 使用 nostr-tools 的統一介面可以簡化實作。

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