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);
}
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.encrypt
as 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);
}
Thanks for your answer, I got the answer I wanted from it!