Web Crypto API вызывает исключение DOMException при расшифровке AES

Я хочу выполнить базовую расшифровку AES-CBC. У меня есть строка encData, зашифрованная 128-битным ключом rawKey, вектор инициализации defaultIV равен нулю. Я хочу использовать только Web Crypto API, без сторонних библиотек. Возможно ли это сделать?

window.crypto.subtle.decrypt Web Crypto API выдает исключение, когда я его использую: DOMException (и никакой дополнительной информации) в Chromium и OperationError: The operation failed for an operation-specific reason в Firefox.

В чем проблема?

Ключ и зашифрованные данные в порядке, я проверил их в онлайн-расшифровке (используйте шестнадцатеричную строку из вывода консоли).


Код:

!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);

}();

person KeyKi    schedule 07.02.2020    source источник
comment
Ожидаемый результат: MEGA{"n":"SharedFile.jpg","c":"GRSM8+c1HUmlmyDuTJVrDwSDpqRV"} Я использовал aes.online-domain-tools.com.   -  person KeyKi    schedule 08.02.2020
comment
DOM Exception — это объектная модель документа. Это ваш браузер жалуется на ваш клиентский код. Я загляну в эту библиотеку.   -  person Sydney Y    schedule 08.02.2020
comment
В Firefox я получаю OperationError: The operation failed for an operation-specific reason.   -  person KeyKi    schedule 08.02.2020
comment
Я сейчас в растерянности. Можете ли вы сказать мне, что вы пытаетесь сделать? Я довольно уверенно разбираюсь в JavaScript, поэтому думаю, что могу помочь, но я не знаком с криптографией. Пример: у меня есть зашифрованная и хешированная строка JSON, которую я пытаюсь декодировать.   -  person Sydney Y    schedule 08.02.2020
comment
Базовая расшифровка AES-CBC. У меня есть строка encData, зашифрованная 128-битным ключом rawKey, вектор инициализации defaultIV равен нулю. Я хотел использовать Web Crypto API, без сторонних библиотек.   -  person KeyKi    schedule 08.02.2020
comment
Круто, спасибо, понял. Я вернусь к вам :)   -  person Sydney Y    schedule 08.02.2020


Ответы (2)


Прокладка (PKCS #7). Это была проблема. Мой зашифрованный текст, который я получаю от стороннего сервиса, не имеет заполнения (которое добавляется перед шифрованием).

Web Crypto API не работает с данными, зашифрованными без заполнения.

AES-JS имеет простой в использовании API, но он работает только с текстом без отступов (в моем случае все в порядке), но если вы используете его для расшифровки зашифрованного текста с отступами, вам нужно вручную удалить его из строки результата .

CryptoJS API выглядит устаревшим, но он отлично работает в обоих случаях, независимо от того, есть ли у текста отступы или нет.

Вам нужно преобразовать ArrayBuffer во что-то другое:

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));
}

person KeyKi    schedule 10.02.2020
comment
Это не важно, но я должен отметить, что моя строка имеет отступ, но это ZeroPadding. Нулей в конце я не увидел в консоли, но они повлияли на длину строки. CryptoJS автоматически удаляет заполнение Pkcs7 (если оно существует) по умолчанию, но не другие типы. Таким образом, вы можете указать тип заполнения в третьем параметре (где iv), например, — padding: CryptoJS.pad.ZeroPadding. - person KeyKi; 11.02.2020

См. этот пост, лучше использовать AES-GCM, если это возможно: an-operati">Расшифровка Webcrypto AES-CBC: ошибка операции — операция не удалась по причине, связанной с операцией

Вы можете проверить, что поддерживает ваша версия браузера, с помощью этого набора тестов — https://peculiarventures.github.io/pv-webcrypto-tests/

Конечно, можно использовать AES-CBC с WebCrypto: https://github.com/diafygi/webcrypto-examples#aes-cbc

person rmhrisk    schedule 09.02.2020