-
Notifications
You must be signed in to change notification settings - Fork 0
/
crypto.js
134 lines (118 loc) · 3.79 KB
/
crypto.js
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
async function createKeys() {
return await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["encrypt", "decrypt"]
);
}
async function saveKeysToLocalStorage(keys) {
try {
const {privateKey, publicKey} = keys;
const exportedPrivateKey = await exportPrivateKeyToBase64(privateKey);
const exportedPublicKey = await exportPublicKeyToBase64(publicKey);
// Store the encrypted private key and expiration timestamp in localStorage
const ttlSeconds = 3600; // 1 hour
const expirationTimestamp = Date.now() + ttlSeconds * 1000;
localStorage.setItem('privateKeyBase64', exportedPrivateKey);
localStorage.setItem('publicKeyBase64', exportedPublicKey);
localStorage.setItem('ttl', expirationTimestamp.toString());
}
catch (e) {
console.error(e);
}
}
async function fetchKeysFromLocalStorage() {
const privateKeyBase64 = localStorage.getItem('privateKeyBase64');
if (privateKeyBase64 === null)
throw new Error("private key not found");
const publicKeyBase64 = localStorage.getItem('publicKeyBase64');
if (publicKeyBase64 === null)
throw new Error("public key not found");
const keyExpiration = parseInt(localStorage.getItem('ttl'));
if (Date.now() < keyExpiration) {
return {privateKey: await importPrivateKey(privateKeyBase64), publicKey: publicKeyBase64};
} else {
throw new Error("keys expired");
}
}
async function exportPublicKeyToBase64(publicKey) {
const exported = await window.crypto.subtle.exportKey("spki", publicKey);
const exportedAsString = ab2str(exported);
const exportedAsBase64 = window.btoa(exportedAsString);
return exportedAsBase64;
}
async function exportPrivateKeyToBase64(privateKey) {
const exportedPrivateKey = await window.crypto.subtle.exportKey("pkcs8", privateKey);
const exportedPrivateKeyAsString = ab2str(exportedPrivateKey);
const exportedPrivateKeyAsBase64 = window.btoa(exportedPrivateKeyAsString);
return exportedPrivateKeyAsBase64;
}
async function importPublicKey(pem) {
// base64 decode the string to get the binary data
const binaryDerString = window.atob(pem);
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString);
return await window.crypto.subtle.importKey(
"spki",
binaryDer,
{
name: "RSA-OAEP",
hash: "SHA-256",
},
true,
["encrypt"]
);
}
async function importPrivateKey(pem) {
const binaryDerString = window.atob(pem);
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString);
return await window.crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSA-OAEP",
hash: "SHA-256",
},
true,
["decrypt"]
);
}
async function encryptMessage(publicKeyBase64, message) {
const publicKey = await importPublicKey(publicKeyBase64);
return await window.crypto.subtle.encrypt(
{name: "RSA-OAEP"},
publicKey,
new TextEncoder().encode(message)
);
}
async function decryptMessage(privateKey, encryptedMessage) {
const decryptedMessage = await window.crypto.subtle.decrypt(
{ name: "RSA-OAEP" },
privateKey,
encryptedMessage
);
return new TextDecoder().decode(decryptedMessage);
}
function encodeEncryptedMessageAsBase64(encryptedMessage) {
return window.btoa(ab2str(encryptedMessage));
}
function decodeBase64EncryptedMessage(base64encodedMessage) {
return str2ab(window.atob(base64encodedMessage));
}