wallet-encrypt-decrypt
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBSV Message Encryption
BSV消息加密
Encrypt and decrypt messages between parties using .
@bsv/sdk使用在各方之间加密和解密消息。
@bsv/sdkRecommended: Use EncryptedMessage from @bsv/sdk
推荐方案:使用@bsv/sdk中的EncryptedMessage
The provides for secure message encryption. This is the preferred approach - avoid rolling custom encryption implementations.
@bsv/sdkEncryptedMessagetypescript
import { PrivateKey, EncryptedMessage, Utils } from '@bsv/sdk'
const sender = PrivateKey.fromRandom()
const recipient = PrivateKey.fromRandom()
// Encrypt: sender uses their private key + recipient's public key
const message = Utils.toArray('Secret message', 'utf8')
const encrypted = EncryptedMessage.encrypt(message, sender, recipient.toPublicKey())
// Decrypt: recipient uses their private key
const decrypted = EncryptedMessage.decrypt(encrypted, recipient)
const plaintext = Utils.toUTF8(decrypted)@bsv/sdkEncryptedMessagetypescript
import { PrivateKey, EncryptedMessage, Utils } from '@bsv/sdk'
const sender = PrivateKey.fromRandom()
const recipient = PrivateKey.fromRandom()
// Encrypt: sender uses their private key + recipient's public key
const message = Utils.toArray('Secret message', 'utf8')
const encrypted = EncryptedMessage.encrypt(message, sender, recipient.toPublicKey())
// Decrypt: recipient uses their private key
const decrypted = EncryptedMessage.decrypt(encrypted, recipient)
const plaintext = Utils.toUTF8(decrypted)Two Encryption Patterns
两种加密模式
Pattern 1: Static-Static ECDH (Both Parties Have Keys)
模式1:静态-静态ECDH(双方均持有密钥)
Use when both parties have established keypairs (e.g., BAP identities, paymail addresses).
typescript
import { PrivateKey, EncryptedMessage, Utils } from '@bsv/sdk'
// Alice and Bob both have persistent keys
const alice = PrivateKey.fromWif('L1...')
const bob = PrivateKey.fromWif('K1...')
// Alice encrypts to Bob
const ciphertext = EncryptedMessage.encrypt(
Utils.toArray('Hello Bob', 'utf8'),
alice,
bob.toPublicKey()
)
// Bob decrypts from Alice
const plaintext = EncryptedMessage.decrypt(ciphertext, bob)Use cases:
- Peer-to-peer encrypted messaging
- Encrypted backups to your own key
- Communication between known identities
适用于双方均已建立密钥对的场景(例如BAP身份、paymail地址)。
typescript
import { PrivateKey, EncryptedMessage, Utils } from '@bsv/sdk'
// Alice and Bob both have persistent keys
const alice = PrivateKey.fromWif('L1...')
const bob = PrivateKey.fromWif('K1...')
// Alice encrypts to Bob
const ciphertext = EncryptedMessage.encrypt(
Utils.toArray('Hello Bob', 'utf8'),
alice,
bob.toPublicKey()
)
// Bob decrypts from Alice
const plaintext = EncryptedMessage.decrypt(ciphertext, bob)适用场景:
- 点对点加密消息传输
- 使用自有密钥进行加密备份
- 已知身份之间的通信
Pattern 2: ECIES-Style (Ephemeral Sender Key)
模式2:ECIES风格(临时发送方密钥)
Use when sender doesn't have a persistent identity (e.g., browser-to-server).
typescript
import { PrivateKey, EncryptedMessage, Utils } from '@bsv/sdk'
// Server has persistent key, client generates ephemeral
const serverKey = PrivateKey.fromWif('L1...')
const ephemeralClient = PrivateKey.fromRandom()
// Client encrypts (ephemeral → server)
const encrypted = EncryptedMessage.encrypt(
Utils.toArray('sensitive data', 'utf8'),
ephemeralClient,
serverKey.toPublicKey()
)
// Include ephemeral public key so server can decrypt
const payload = {
ephemeralPub: ephemeralClient.toPublicKey().toString(),
ciphertext: Utils.toHex(encrypted)
}
// Server decrypts using ephemeral pubkey
const clientPub = PublicKey.fromString(payload.ephemeralPub)
// Note: EncryptedMessage.decrypt needs the sender info embedded in ciphertextUse cases:
- Browser-to-server encryption
- One-way secure channels
- Forward secrecy (fresh key per message)
适用于发送方无持久身份的场景(例如浏览器到服务器的通信)。
typescript
import { PrivateKey, EncryptedMessage, Utils } from '@bsv/sdk'
// Server has persistent key, client generates ephemeral
const serverKey = PrivateKey.fromWif('L1...')
const ephemeralClient = PrivateKey.fromRandom()
// Client encrypts (ephemeral → server)
const encrypted = EncryptedMessage.encrypt(
Utils.toArray('sensitive data', 'utf8'),
ephemeralClient,
serverKey.toPublicKey()
)
// Include ephemeral public key so server can decrypt
const payload = {
ephemeralPub: ephemeralClient.toPublicKey().toString(),
ciphertext: Utils.toHex(encrypted)
}
// Server decrypts using ephemeral pubkey
const clientPub = PublicKey.fromString(payload.ephemeralPub)
// Note: EncryptedMessage.decrypt needs the sender info embedded in ciphertext适用场景:
- 浏览器到服务器的加密通信
- 单向安全通道
- 前向保密(每条消息使用新密钥)
How ECDH Key Agreement Works
ECDH密钥协商的工作原理
Both parties derive the same shared secret:
Alice: alicePrivKey × bobPubKey = sharedPoint
Bob: bobPrivKey × alicePubKey = sharedPointThe shared point is then used to derive a symmetric key for AES-256-GCM encryption.
双方会生成相同的共享密钥:
Alice: alicePrivKey × bobPubKey = sharedPoint
Bob: bobPrivKey × alicePubKey = sharedPoint随后,该共享点会被用于生成AES-256-GCM加密所需的对称密钥。
API Reference
API参考
EncryptedMessage.encrypt()
EncryptedMessage.encrypt()
typescript
static encrypt(
message: number[], // Plaintext as byte array
sender: PrivateKey, // Sender's private key
recipient: PublicKey // Recipient's public key
): number[] // Encrypted bytestypescript
static encrypt(
message: number[], // Plaintext as byte array
sender: PrivateKey, // Sender's private key
recipient: PublicKey // Recipient's public key
): number[] // Encrypted bytesEncryptedMessage.decrypt()
EncryptedMessage.decrypt()
typescript
static decrypt(
ciphertext: number[], // Encrypted bytes from encrypt()
recipient: PrivateKey // Recipient's private key
): number[] // Decrypted plaintext bytestypescript
static decrypt(
ciphertext: number[], // Encrypted bytes from encrypt()
recipient: PrivateKey // Recipient's private key
): number[] // Decrypted plaintext bytesUtility Functions
工具函数
typescript
// String to bytes
Utils.toArray('hello', 'utf8') // number[]
// Bytes to string
Utils.toUTF8([104, 101, 108, 108, 111]) // 'hello'
// Bytes to hex
Utils.toHex([1, 2, 3]) // '010203'
// Hex to bytes
Utils.toArray('010203', 'hex') // [1, 2, 3]typescript
// String to bytes
Utils.toArray('hello', 'utf8') // number[]
// Bytes to string
Utils.toUTF8([104, 101, 108, 108, 111]) // 'hello'
// Bytes to hex
Utils.toHex([1, 2, 3]) // '010203'
// Hex to bytes
Utils.toArray('010203', 'hex') // [1, 2, 3]Security Properties
安全特性
- Authenticated encryption: AES-256-GCM provides confidentiality + integrity
- Key agreement: ECDH ensures only intended recipient can decrypt
- No key transmission: Private keys never leave their owner
- 认证加密:AES-256-GCM同时提供机密性与完整性保障
- 密钥协商:ECDH确保只有目标接收方能够解密消息
- 无密钥传输:私钥永远不会离开其所有者
Common Mistakes
常见误区
Don't roll your own crypto
不要自行实现加密逻辑
❌ Wrong: Implementing ECDH + AES manually
typescript
// Don't do this - use EncryptedMessage instead
const sharedSecret = myPrivKey.deriveSharedSecret(theirPubKey)
const aesKey = sha256(sharedSecret)
// ... manual AES encryption✅ Correct: Use the SDK's built-in class
typescript
const encrypted = EncryptedMessage.encrypt(message, sender, recipient)❌ 错误做法:手动实现ECDH + AES
typescript
// Don't do this - use EncryptedMessage instead
const sharedSecret = myPrivKey.deriveSharedSecret(theirPubKey)
const aesKey = sha256(sharedSecret)
// ... manual AES encryption✅ 正确做法:使用SDK内置的类
typescript
const encrypted = EncryptedMessage.encrypt(message, sender, recipient)Don't confuse encryption with authentication
不要混淆加密与认证
- Encryption (this skill): Hides message content
- Authentication (): Proves sender identity
bitcoin-auth
For authenticated + encrypted communication, use both:
typescript
// Encrypt the payload
const encrypted = EncryptedMessage.encrypt(payload, sender, recipient)
// Sign the request (proves sender identity)
const authToken = getAuthToken({ privateKeyWif, requestPath, body })- 加密(本技能):隐藏消息内容
- 认证():证明发送方身份
bitcoin-auth
如需同时实现认证与加密的通信,请结合使用两者:
typescript
// Encrypt the payload
const encrypted = EncryptedMessage.encrypt(payload, sender, recipient)
// Sign the request (proves sender identity)
const authToken = getAuthToken({ privateKeyWif, requestPath, body })