Reputation: 116
I'm attempting to use SubtleCrypto to encrypt a string, store the encrypted string, and decrypt that string again, all using a generated RSA-OAEP key pair.
The below code produces a DOMException during the decryption phase, however I can't seem to get any detail on the error. I've tried using "SHA-1" for hashing but have the same issue.
Any hints?
let encoder = new TextEncoder();
let decoder = new TextDecoder('utf-8');
// Generate a key pair
let keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 4096,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["encrypt", "decrypt"]
);
let publicKey = await crypto.subtle.exportKey('jwk', keyPair.publicKey);
let privateKey = await crypto.subtle.exportKey('jwk', keyPair.privateKey);
// Encrypt a string
let encodedSecret = encoder.encode("MYSECRETVALUE");
let pubcryptokey = await window.crypto.subtle.importKey(
'jwk',
publicKey,
{
name: "RSA-OAEP",
hash: "SHA-256"
},
false,
["encrypt"]
);
let encrypted = await window.crypto.subtle.encrypt(
{
name: "RSA-OAEP"
},
pubcryptokey,
encodedSecret
);
let encDV = new DataView(encrypted);
let ct = decoder.decode(encDV);
// Decrypt the string
let encodedCiphertext = encoder.encode(ct);
let privcryptokey = await window.crypto.subtle.importKey(
'jwk',
privateKey,
{
name: "RSA-OAEP",
hash: "SHA-256"
},
false,
["decrypt"]
);
console.log("Before decrypt");
let decrypted = await window.crypto.subtle.decrypt(
{
name: "RSA-OAEP"
},
privcryptokey,
encodedCiphertext
);
console.log("After decrypt");
let decDV = new DataView(decrypted);
let pt = decoder.decode(decDV);
console.log(pt);
Upvotes: 1
Views: 1062
Reputation: 49121
The problem is the UTF-8 encoding/decoding of the ciphertext, which corrupts the data. If arbitrary binary data, such as ciphertexts, is to be stored in a string, a binary-to-text encoding such as Base64 must be used, see e.g. here.
If this is fixed, decryption works:
(async () => {
let encoder = new TextEncoder();
let decoder = new TextDecoder('utf-8');
// Generate a key pair
let keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 4096,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["encrypt", "decrypt"]
);
let publicKey = await crypto.subtle.exportKey('jwk', keyPair.publicKey);
let privateKey = await crypto.subtle.exportKey('jwk', keyPair.privateKey);
// Encrypt a string
let encodedSecret = encoder.encode("MYSECRETVALUE");
let pubcryptokey = await window.crypto.subtle.importKey(
'jwk',
publicKey,
{
name: "RSA-OAEP",
hash: "SHA-256"
},
false,
["encrypt"]
);
let encrypted = await window.crypto.subtle.encrypt(
{
name: "RSA-OAEP"
},
pubcryptokey,
encodedSecret
);
//let encDV = new DataView(encrypted); // Bug: UTF-8 decoding damages the ciphertext
//let ct = decoder.decode(encDV);
let ct = ab2b64(encrypted); // Fix: Use a binary to text encoding like Base64
console.log(ct.replace(/(.{48})/g,'$1\n'));
// Decrypt the string
//let encodedCiphertext = encoder.encode(ct); // Bug: s. above
let encodedCiphertext = b642ab(ct); // Fix: s. above
let privcryptokey = await window.crypto.subtle.importKey(
'jwk',
privateKey,
{
name: "RSA-OAEP",
hash: "SHA-256"
},
false,
["decrypt"]
);
console.log("Before decrypt");
let decrypted = await window.crypto.subtle.decrypt(
{
name: "RSA-OAEP"
},
privcryptokey,
encodedCiphertext
);
console.log("After decrypt");
let decDV = new DataView(decrypted);
let pt = decoder.decode(decDV);
console.log(pt);
})();
// https://stackoverflow.com/a/11562550/9014097
function ab2b64(arrayBuffer) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));
}
// https://stackoverflow.com/a/41106346
function b642ab(base64string){
return Uint8Array.from(atob(base64string), c => c.charCodeAt(0));
}
Upvotes: 1