|
|
|
async function encryptDataSaveKey() { |
|
var data = await makeData(); |
|
console.log("generated data", data); |
|
var keys = await makeKeys() |
|
var encrypted = await encrypt(data, keys); |
|
callOnStore(function (store) { |
|
store.put({id: 1, keys: keys, encrypted: encrypted}); |
|
}) |
|
} |
|
|
|
function loadKeyDecryptData() { |
|
callOnStore(function (store) { |
|
var getData = store.get(1); |
|
getData.onsuccess = async function() { |
|
var keys = getData.result.keys; |
|
var encrypted = getData.result.encrypted; |
|
var data = await decrypt(encrypted, keys); |
|
console.log("decrypted data", data); |
|
}; |
|
}) |
|
} |
|
|
|
function callOnStore(fn_) { |
|
|
|
// This works on all devices/browsers, and uses IndexedDBShim as a final fallback |
|
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB; |
|
|
|
// Open (or create) the database |
|
var open = indexedDB.open("MyDatabase", 1); |
|
|
|
// Create the schema |
|
open.onupgradeneeded = function() { |
|
var db = open.result; |
|
var store = db.createObjectStore("MyObjectStore", {keyPath: "id"}); |
|
}; |
|
|
|
|
|
open.onsuccess = function() { |
|
// Start a new transaction |
|
var db = open.result; |
|
var tx = db.transaction("MyObjectStore", "readwrite"); |
|
var store = tx.objectStore("MyObjectStore"); |
|
|
|
fn_(store) |
|
|
|
|
|
// Close the db when the transaction is done |
|
tx.oncomplete = function() { |
|
db.close(); |
|
}; |
|
} |
|
} |
|
|
|
async function encryptDecrypt() { |
|
var data = await makeData(); |
|
console.log("generated data", data); |
|
var keys = await makeKeys() |
|
var encrypted = await encrypt(data, keys); |
|
console.log("encrypted", encrypted); |
|
var finalData = await decrypt(encrypted, keys); |
|
console.log("decrypted data", data); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function makeData() { |
|
return window.crypto.getRandomValues(new Uint8Array(16)) |
|
} |
|
|
|
function makeKeys() { |
|
return window.crypto.subtle.generateKey( |
|
{ |
|
name: "RSA-OAEP", |
|
modulusLength: 2048, //can be 1024, 2048, or 4096 |
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), |
|
hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" |
|
}, |
|
false, //whether the key is extractable (i.e. can be used in exportKey) |
|
["encrypt", "decrypt"] //must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"] |
|
) |
|
} |
|
|
|
function encrypt(data, keys) { |
|
return window.crypto.subtle.encrypt( |
|
{ |
|
name: "RSA-OAEP", |
|
//label: Uint8Array([...]) //optional |
|
}, |
|
keys.publicKey, //from generateKey or importKey above |
|
data //ArrayBuffer of data you want to encrypt |
|
) |
|
} |
|
|
|
|
|
async function decrypt(data, keys) { |
|
return new Uint8Array(await window.crypto.subtle.decrypt( |
|
{ |
|
name: "RSA-OAEP", |
|
//label: Uint8Array([...]) //optional |
|
}, |
|
keys.privateKey, //from generateKey or importKey above |
|
data //ArrayBuffer of the data |
|
)); |
|
} |
@erdum I built a web-based end-to-end encrypted messaging app using this. The user generates their keypair and then backs up their private key (it gets password encrypted first) to file. It has worked well. Is it perfect? No. Do you have to trust the website isn't uploading the user's private key when they generate it? Yes.
Obviously, I trust my own system. It is as safe as the user's browser is though.