javascriptencryptionaeswindow.crypto

Decrypt a AES-CTR 256 bits message from browser with window.crypto.subtle APIs


I need to decrypt in the browser a message encoded with AES-CTR 256 bits (encoded using OpenSSL).

Using OpenSSL I get something like:

key=189BBBB00C5F1FB7FBA9AD9285F193D1771D7611CB891E5C1F4E24C20E50FB1D
iv =4103C88663AE12CE18EA46E894280C4D
msg=nhVKeu8zNO2PRTwJrDE=

Well, my problem is converting those strings into objects the window.crypto.subtle APIs can manage. Eg.

const counter = ???;
const ciphertext = ???;
const rawKey = ???;

const key = window.crypto.subtle.importKey(
    "raw",
    key,
    "AES-CTR",
    true,
    ["encrypt", "decrypt"]
);

const decrypted = await window.crypto.subtle.decrypt(
{
    name: "AES-CTR",
    counter,
    length: 64
  },
  key,
  ciphertext
);

let dec = new TextDecoder();
const msg = dec.decode(decrypted);
console.log(msg);

Could anyone help me passing from key, iv, msg to counter, ciphertext, rawkey?

Thank you very much


Solution

  • Key, counter (or IV) and ciphertext can be passed as TypedArray, i.e. you need two conversions, one from a hexadecimal, and a second from a Base64 encoded string into a TypedArray, e.g.

    from a hexadecimal encoded string, here:

    const fromHex = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
    

    from a Base64 encoded string, here:

    const fromBase64 = base64String => Uint8Array.from(atob(base64String), c => c.charCodeAt(0));
    

    In the code itself an await operator is missing and in the importKey function rawKey must be used instead of key (probably copy/paste errors). Alltogether:

    const fromHex = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
    const fromBase64 = base64String => Uint8Array.from(atob(base64String), c => c.charCodeAt(0));
    		
    async function test(){
    		
        const rawKey = fromHex("189BBBB00C5F1FB7FBA9AD9285F193D1771D7611CB891E5C1F4E24C20E50FB1D");
        const counter = fromHex("4103C88663AE12CE18EA46E894280C4D");
        const ciphertext = fromBase64("nhVKeu8zNO2PRTwJrDE=");
    
        const key = await window.crypto.subtle.importKey(   // add >await<
            "raw",
            rawKey,                                         // replace >key< with >rawKey<
            "AES-CTR",
            true,
            ["encrypt", "decrypt"]
        );
    
        const decrypted = await window.crypto.subtle.decrypt(
            {
                name: "AES-CTR",
                counter,
                length: 64
            },
            key,
            ciphertext
        );
    
        let dec = new TextDecoder();
        const msg = dec.decode(decrypted);
        console.log(msg);
    }
    
    test();

    This decrypts the ciphertext to:

    hello, world!