AES-шифрование и дешифрование файлов с использованием crypto- js - PullRequest
1 голос
/ 04 марта 2020

В настоящее время у меня есть такое требование, но я не могу расшифровать зашифрованный файл, созданный файл всегда поврежден и не может быть открыт, и я не знаю, в чем проблема. Код, размещенный ниже, является кодом, который я использую в VUE.

Encode() {
           let CryptoJS = require("crypto-js");
            this.file_mime = this.file.type;
            this.file_name = this.file.name;
            let reader = new FileReader();
            reader.onload = () => {
                let key = "1234567887654321";
                // let wordArray = CryptoJS.lib.WordArray.create(reader.result);
                // let plaintext = CryptoJS.enc.Hex.stringify(wordArray);
                let encrypted = CryptoJS.AES.encrypt(reader.result, key).toString();

                this.file2 = new Blob([encrypted], {
                    type: this.file_mime
                });
                const a = document.createElement("a");
                const url = window.URL.createObjectURL(this.file2);
                const filename = this.file_name;
                a.href = url;
                a.download = filename;
                a.click();
                window.URL.revokeObjectURL(url);
            };
            reader.readAsBinaryString(this.file);
        }

Decode() {
            let CryptoJS = require("crypto-js");
            let reader = new FileReader();
            reader.onload = () => {
                let key = "1234567887654321";
                let decrypted = CryptoJS.AES.decrypt(reader.result, key).toString(CryptoJS.enc.Utf8)
                this.file2 = new Blob([decrypted], {type: this.file_mime});
                const a = document.createElement("a");
                const url = window.URL.createObjectURL(this.file2);
                const filename = this.file_name;
                a.href = url;
                a.download = filename;
                a.click();
                window.URL.revokeObjectURL(url);
            };
            reader.readAsBinaryString(this.file);
        }

1 Ответ

0 голосов
/ 05 марта 2020

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