Warm tip: This article is reproduced from stackoverflow.com, please click
aes cryptojs javascript

AES encryption and decryption of files using crypto-js

发布于 2020-05-21 16:52:50

I currently have such a requirement, but I cannot decrypt the encrypted file, the generated file is always damaged and cannot be opened, and I do not know where the problem lies. The code posted below is the code I use in 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);
        }
Questioner
Young Xu
Viewed
320
Topaco 2020-03-06 00:37

In JavaScript there are several types for arrays: Array, ArrayBuffer and typed arrays. CryptoJS also uses WordArray. You have to convert between these types properly.

For encryption, FileReader.readAsBinaryString should be replaced by FileReader.readAsArrayBuffer, which returns the binary data from the file as an ArrayBuffer. In the encryption-method the ArrayBuffer can be converted into a WordArray which can be processed directly by CryptoJS.AES.encrypt. CryptoJS.AES.encrypt returns the ciphertext as CipherParams object (here), which is converted with toString() into a Base64 encoded string in OpenSSL format. This string can be used to directly create the blob.

Note: Since the key is passed in CryptoJS.AES.encryptas a string, it is interpreted as a passphrase and a random 8 bytes salt is generated applying the same algorithm that OpenSSL uses, from which, together with the passphrase, key and IV are generated. The ciphertext is preceded by a 16 bytes block consisting of the ASCII encoding of Salted__, followed by the 8 bytes salt, and the whole stuff is Base64 encoded. In this OpenSSL format the Base64 encoded data therefore starts with U2FsdGVkX1, here and here.

Changes in the encryption-method:

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

Note: Since the encrypted data are Base64 encoded and the Base64 encoding has an overhead of 33%, the encrypted data are correspondingly larger compared to the unencrypted data.

For decryption, FileReader.readAsBinaryString should be replaced by FileReader.readAsText, because the encrypted data are stored as a Base64 encoded string (in OpenSSL format), which can be passed directly to CryptoJS.AES.decrypt. This string is implicitly converted into a CipherParams object (here, alternatively, a CipherParams object can be explicitly passed). CryptoJS.AES.decrypt returns the decrypted data as WordArray, so that a conversion into a typed array or ArrayBuffer is necessary to create the blob. For this the following function can be used:

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

Note: A conversion of the WordArray into an Utf8-string (as in the posted code) is not possible and generally corrupts the data, because binary data (e.g. from a pdf) contain arbitrary byte sequences that generally do not correspond to Utf8 encodings (because binary data do not contain printable characters), here.

Changes in the decryption-method:

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