Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web Crypto API throws `DOMException` on AES decryption

I want to perform basic AES-CBC decryption. I have the string encData that is encrypted with the 128-bits key rawKey, the initialization vector defaultIV is zero. I want to use only Web Crypto API, without 3th-party libraries. Is it possible to do?

window.crypto.subtle.decrypt of Web Crypto API throws the exception when I use it: DOMException (and no more information) in Chromium and OperationError: The operation failed for an operation-specific reason in Firefox.

What is it the problem?

The key and encrypted data is OK, I have checked it in an online decryption (use the hex string from the console output).


The code:

!async function script() {

// ArrayBuffer to Hex String. https://stackoverflow.com/a/40031979/11468937
function buf2hex(buffer) {
    return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}


const defaultIV = new Uint8Array(16);
const rawKey    = new Uint8Array([42, 40, 254, 9, 99, 201, 174, 52, 226, 21, 90, 155, 81, 50, 2, 9]);
const encData   = new Uint8Array([102, 80, 220, 73, 185, 233, 85, 7, 195, 196, 137, 107, 65, 150, 162, 161, 80, 82, 26, 18, 110, 247, 189, 176, 35, 197, 140, 4, 138, 75, 159, 197, 75, 88, 131, 23, 235, 125, 96, 81, 41, 170, 220, 45, 64, 55, 30, 68, 39, 6, 112, 194, 243, 209, 177, 173, 54, 71, 21, 172, 62, 147, 112, 76]);

console.log("defaultIV\n", defaultIV, buf2hex(defaultIV));
console.log("rawKey\n",    rawKey,    buf2hex(rawKey));
console.log("encData\n",   encData,   buf2hex(encData));


const key = await crypto.subtle.importKey(
    "raw",
    rawKey,
    "AES-CBC",
    true,
    ["decrypt"]
);
console.log("key", key);


// It throws "Uncaught (in promise) DOMException"
const decrypted  = await crypto.subtle.decrypt(
    {
        name: "AES-CBC",
        iv: defaultIV
    },
    key,
    encData
);
console.log("decrypted", decrypted);

}();
like image 384
KeyKi Avatar asked Feb 07 '20 23:02

KeyKi


1 Answers

A padding (PKCS #7). It was the problem. My encrypted text that I get from a 3th-party service has no padding (that adds before encryption).

Web Crypto API does not work with data encrypted without the padding.

AES-JS has easy to use API, but it works only with a text without the padding (In my case it's OK), but if you use it to decrypt a cipher text with the padding you need to manually remove it from the result string.

CryptoJS API looks ancient, but it works fine in both case, when the text has the padding or not.

You need to transform ArrayBuffer to something other:

let key       = ab_to_hex(keyArrayBuffer);
let iv        = ab_to_bin_str(ivArrayBuffer);
let encrypted = ab_to_bin_str(encryptedArrayBuffer);

function decrypt(key, iv, encrypted) {
    const plaintextArray = CryptoJS.AES.decrypt(
        { ciphertext: CryptoJS.enc.Latin1.parse(encrypted) },
        CryptoJS.enc.Hex.parse(key),
        { iv: CryptoJS.enc.Latin1.parse(iv) }
    );
    return CryptoJS.enc.Utf8.stringify(plaintextArray);
}

function ab_to_hex(buffer) {
    return Array.from(new Uint8Array(buffer)).map(n => ("0" + n.toString(16)).slice(-2)).join("");
}

function ab_to_bin_str(buffer) {
    return String.fromCharCode(...new Uint8Array(buffer));
}

like image 116
KeyKi Avatar answered Oct 08 '22 11:10

KeyKi