1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
| class E2EChat {
constructor() {
this.keyPair = null
this.peerKeys = new Map()
}
async init() {
// 生成密钥对
this.keyPair = await crypto.subtle.generateKey(
{
name: 'RSA-OAEP',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256'
},
true,
['encrypt', 'decrypt']
)
}
async getPublicKey() {
const exported = await crypto.subtle.exportKey('spki', this.keyPair.publicKey)
return btoa(String.fromCharCode(...new Uint8Array(exported)))
}
async addPeer(id, publicKeyData) {
const binaryString = atob(publicKeyData)
const bytes = new Uint8Array(binaryString.length)
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i)
}
const publicKey = await crypto.subtle.importKey(
'spki',
bytes,
{ name: 'RSA-OAEP', hash: 'SHA-256' },
true,
['encrypt']
)
this.peerKeys.set(id, publicKey)
}
async sendMessage(peerId, message) {
const publicKey = this.peerKeys.get(peerId)
if (!publicKey) throw new Error('Peer not found')
// 生成会话密钥
const sessionKey = await this.generateSessionKey()
// 加密消息
const encryptedMessage = await this.encryptMessage(message, sessionKey)
// 加密会话密钥
const encryptedKey = await this.encryptSessionKey(sessionKey, publicKey)
return {
message: encryptedMessage,
key: encryptedKey
}
}
async receiveMessage(data) {
// 解密会话密钥
const sessionKey = await this.decryptSessionKey(data.key)
// 解密消息
return await this.decryptMessage(data.message, sessionKey)
}
async generateSessionKey() {
return await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
)
}
async encryptMessage(message, key) {
const encoder = new TextEncoder()
const iv = crypto.getRandomValues(new Uint8Array(12))
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: iv },
key,
encoder.encode(message)
)
return {
data: btoa(String.fromCharCode(...new Uint8Array(encrypted))),
iv: btoa(String.fromCharCode(...iv))
}
}
async decryptMessage(data, key) {
const ciphertext = Uint8Array.from(atob(data.data), c => c.charCodeAt(0))
const iv = Uint8Array.from(atob(data.iv), c => c.charCodeAt(0))
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: iv },
key,
ciphertext
)
const decoder = new TextDecoder()
return decoder.decode(decrypted)
}
}
|