跳至主要內容
進階

NIP-04 加密私訊

深入了解 Nostr 的加密直接訊息規範,ECDH 密鑰交換和 AES-256-CBC 加密。

15 分鐘

NIP-04 概述

NIP-04 定義了 Nostr 中的加密直接訊息(Encrypted Direct Messages)格式。 它使用 ECDH(橢圓曲線 Diffie-Hellman)密鑰交換生成共享密鑰, 然後用 AES-256-CBC 加密訊息內容。

安全警告: NIP-04 存在一些已知的安全限制,包括缺乏前向保密和元數據洩露。 NIP-44 是更安全的替代方案,但 NIP-04 仍被廣泛使用。

事件結構

{
  "id": "...",
  "pubkey": "<發送者公鑰>",
  "created_at": 1234567890,
  "kind": 4,                          // 加密 DM 類型
  "tags": [
    ["p", "<接收者公鑰>"]            // 必須標記接收者
  ],
  "content": "<加密內容>?iv=<初始向量>",
  "sig": "..."
}

加密流程

1

ECDH 密鑰交換

使用發送者私鑰和接收者公鑰計算共享密鑰:shared = ECDH(私鑰_A, 公鑰_B)

2

生成 AES 密鑰

取共享點的 x 座標作為 AES-256 密鑰

3

生成隨機 IV

生成 16 bytes 的隨機初始向量

4

AES-CBC 加密

使用 AES-256-CBC 模式加密訊息內容

5

組合輸出

將加密內容和 IV 用 Base64 編碼,格式:<encrypted>?iv=<iv>

程式碼範例

加密訊息

import * as secp256k1 from '@noble/secp256k1'
import { randomBytes, createCipheriv } from 'crypto'

function encrypt(privateKey, recipientPubkey, message) {
  // 1. ECDH 計算共享密鑰
  const sharedPoint = secp256k1.getSharedSecret(
    privateKey,
    '02' + recipientPubkey  // 壓縮公鑰格式
  )
  const sharedKey = sharedPoint.slice(1, 33)  // 取 x 座標

  // 2. 生成隨機 IV
  const iv = randomBytes(16)

  // 3. AES-256-CBC 加密
  const cipher = createCipheriv('aes-256-cbc', sharedKey, iv)
  let encrypted = cipher.update(message, 'utf8', 'base64')
  encrypted += cipher.final('base64')

  // 4. 組合輸出
  return encrypted + '?iv=' + iv.toString('base64')
}

解密訊息

import { createDecipheriv } from 'crypto'

function decrypt(privateKey, senderPubkey, content) {
  // 1. 解析加密內容和 IV
  const [encrypted, ivParam] = content.split('?iv=')
  const iv = Buffer.from(ivParam, 'base64')

  // 2. ECDH 計算共享密鑰(與加密相同)
  const sharedPoint = secp256k1.getSharedSecret(
    privateKey,
    '02' + senderPubkey
  )
  const sharedKey = sharedPoint.slice(1, 33)

  // 3. AES-256-CBC 解密
  const decipher = createDecipheriv('aes-256-cbc', sharedKey, iv)
  let decrypted = decipher.update(encrypted, 'base64', 'utf8')
  decrypted += decipher.final('utf8')

  return decrypted
}

安全特性

優點

  • • 端到端加密
  • • 無需密鑰交換儀式
  • • 中繼器無法讀取內容
  • • 實現簡單

限制

  • • 無前向保密(PFS)
  • • 元數據可見(誰和誰通訊)
  • • 無訊息認證(MAC)
  • • 重放攻擊風險

ECDH 原理

ECDH 允許兩方在不交換私鑰的情況下建立共享密鑰:

# Alice 和 Bob 各有密鑰對
Alice: 私鑰 a, 公鑰 A = a * G
Bob:   私鑰 b, 公鑰 B = b * G

# 計算共享密鑰
Alice: shared = a * B = a * (b * G) = ab * G
Bob:   shared = b * A = b * (a * G) = ab * G

# 相同的共享點!

NIP-44:更安全的替代

NIP-44 解決了 NIP-04 的多個安全問題:

特性 NIP-04 NIP-44
加密算法 AES-256-CBC XChaCha20-Poly1305
訊息認證 有(AEAD)
填充 PKCS7 固定長度
版本控制

查詢私訊

# 查詢發送給我的私訊
["REQ", "my-dms", {
  "kinds": [4],
  "#p": ["<my_pubkey>"]
}]

# 查詢我發送的私訊
["REQ", "sent-dms", {
  "kinds": [4],
  "authors": ["<my_pubkey>"]
}]

# 查詢與特定用戶的對話
["REQ", "convo", {
  "kinds": [4],
  "authors": ["<my_pubkey>", "<their_pubkey>"],
  "#p": ["<my_pubkey>", "<their_pubkey>"]
}]

最佳實踐: 對於敏感通訊,考慮使用支持 NIP-44 的客戶端。 同時,定期更換密鑰可以降低長期密鑰洩露的風險。

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