1

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

Daniele Pallastrelli
  • 2,430
  • 1
  • 21
  • 37

1 Answers1

2

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!
Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thank you very much for your answer. Unfortunately, on chrome I got the error: "await is only valid in async function". However, if I remove the "await" (both), I got: "Failed to execute 'decode' on 'TextDecoder': The provided value is not of type '(ArrayBuffer or ArrayBufferView)" – Daniele Pallastrelli Apr 06 '20 at 08:22
  • `await` must be used in the context of an `async` function, see the full code in the updated solution. I have tested this solution in Chrome and it works there. So please try this. Note also, that `async/await` are syntactic sugar for promise use, see e.g. [here](https://stackoverflow.com/a/42736964/9014097) and [here](https://medium.com/better-programming/should-i-use-promises-or-async-await-126ab5c98789). – Topaco Apr 06 '20 at 08:47
  • Topaco, any chance you can help me with this question, too? https://stackoverflow.com/questions/61096703/decrypt-an-rsa-message-from-browser-with-window-crypto-subtle-apis?noredirect=1#comment108108734_61096703 – Daniele Pallastrelli Apr 09 '20 at 07:33