В JavaScript существует несколько типов массивов: Array
, ArrayBuffer
и типизированные массивы . Crypto JS также использует WordArray
. Необходимо правильно преобразовать эти типы.
Для шифрования FileReader.readAsBinaryString
следует заменить на FileReader.readAsArrayBuffer
, который возвращает двоичные данные из файла как ArrayBuffer
. В методе шифрования ArrayBuffer
можно преобразовать в WordArray
, который может быть обработан непосредственно с помощью CryptoJS.AES.encrypt
. CryptoJS.AES.encrypt
возвращает зашифрованный текст как CipherParams
объект ( здесь ), который преобразуется с toString()
в строку в кодировке Base64 в формате OpenSSL. Эта строка может быть использована для непосредственного создания BLOB-объекта.
Примечание. Поскольку ключ передается в CryptoJS.AES.encrypt
как строка, он интерпретируется как фраза-пароль и генерируется случайная 8-байтовая соль, используя тот же алгоритм, который использует OpenSSL, из которого вместе с парольной фразой , ключ и IV генерируются. Зашифрованному тексту предшествует 16-байтовый блок, состоящий из кодировки ASCII Salted__
, за которой следует 8-байтовая соль, а весь материал кодируется Base64. Поэтому в этом формате OpenSSL закодированные в Base64 данные начинаются с U2FsdGVkX1
, здесь и здесь .
Изменения в методе шифрования:
function encrypt(input) {
var file = input.files[0];
var reader = new FileReader();
reader.onload = () => {
var key = "1234567887654321";
var wordArray = CryptoJS.lib.WordArray.create(reader.result); // Convert: ArrayBuffer -> WordArray
var encrypted = CryptoJS.AES.encrypt(wordArray, key).toString(); // Encryption: I: WordArray -> O: -> Base64 encoded string (OpenSSL-format)
var fileEnc = new Blob([encrypted]); // Create blob from string
var a = document.createElement("a");
var url = window.URL.createObjectURL(fileEnc);
var filename = file.name + ".enc";
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
};
reader.readAsArrayBuffer(file);
}
Примечание. Поскольку зашифрованные данные кодируются Base64, а кодировка Base64 имеет служебную нагрузку 33% , зашифрованный данные соответственно больше по сравнению с незашифрованными данными.
Для расшифровки FileReader.readAsBinaryString
следует заменить на FileReader.readAsText
, поскольку зашифрованные данные хранятся в виде строки в кодировке Base64 (в OpenSSL формат), который может быть передан непосредственно в CryptoJS.AES.decrypt
. Эта строка неявно преобразуется в объект CipherParams
( здесь , в качестве альтернативы, объект CipherParams
может быть явно передан). CryptoJS.AES.decrypt
возвращает расшифрованные данные как WordArray
, поэтому для создания большого двоичного объекта необходимо преобразование в типизированный массив или ArrayBuffer
. Для этого может использоваться следующая функция:
function convertWordArrayToUint8Array(wordArray) {
var arrayOfWords = wordArray.hasOwnProperty("words") ? wordArray.words : [];
var length = wordArray.hasOwnProperty("sigBytes") ? wordArray.sigBytes : arrayOfWords.length * 4;
var uInt8Array = new Uint8Array(length), index=0, word, i;
for (i=0; i<length; i++) {
word = arrayOfWords[i];
uInt8Array[index++] = word >> 24;
uInt8Array[index++] = (word >> 16) & 0xff;
uInt8Array[index++] = (word >> 8) & 0xff;
uInt8Array[index++] = word & 0xff;
}
return uInt8Array;
}
Примечание. Преобразование WordArray
в строку Utf8 (как в опубликованном коде) невозможно и обычно приводит к повреждению данных, поскольку двоичные данные данные (например, из pdf) содержат произвольные последовательности байтов, которые обычно не соответствуют кодировкам Utf8 (поскольку двоичные данные не содержат печатных символов), здесь .
Изменения в методе дешифрования:
function decrypt(input) {
var file = input.files[0];
var reader = new FileReader();
reader.onload = () => {
var key = "1234567887654321";
var decrypted = CryptoJS.AES.decrypt(reader.result, key); // Decryption: I: Base64 encoded string (OpenSSL-format) -> O: WordArray
var typedArray = convertWordArrayToUint8Array(decrypted); // Convert: WordArray -> typed array
var fileDec = new Blob([typedArray]); // Create blob from typed array
var a = document.createElement("a");
var url = window.URL.createObjectURL(fileDec);
var filename = file.name.substr(0, file.name.length - 4) + ".dec";
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
};
reader.readAsText(file);
}