【问题标题】:AES encryption and decryption of files using crypto-js使用crypto-js对文件进行AES加密和解密
【发布时间】:2020-06-16 14:51:30
【问题描述】:

我目前有这样的需求,但是无法解密加密文件,生成的文件总是损坏无法打开,不知道问题出在哪里。下面贴出的代码是我在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);
        }

【问题讨论】:

    标签: javascript aes cryptojs


    【解决方案1】:

    在 JavaScript 中,数组有几种类型:Array、ArrayBuffer 和 typed arrays。 CryptoJS 也使用WordArray。您必须正确地在这些类型之间进行转换。

    对于加密,FileReader.readAsBinaryString 应替换为FileReader.readAsArrayBuffer,它将文件中的二进制数据作为ArrayBuffer 返回。在加密方法中,ArrayBuffer 可以转换为WordArray,CryptoJS.AES.encrypt 可以直接对其进行处理。 CryptoJS.AES.encrypt 将密文作为CipherParams 对象(here)返回,它与toString() 一起转换为OpenSSL 格式的Base64 编码字符串。此字符串可用于直接创建 blob。

    注意:由于密钥在CryptoJS.AES.encrypt 中作为字符串传递,因此它被解释为密码短语,并应用 OpenSSL 使用的相同算法生成随机的 8 字节盐,从该算法中,与密码短语一起,密钥和IV 生成。密文前面是一个 16 字节的块,由Salted__ 的 ASCII 编码组成,然后是 8 字节的盐,整个东西都是 Base64 编码的。因此,在这种 OpenSSL 格式中,Base64 编码数据以U2FsdGVkX1、here 和here 开头。

    加密方式的变化:

    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编码有overhead of 33%,因此加密后的数据相对于未加密的数据更大。

    对于解密,FileReader.readAsBinaryString 应替换为FileReader.readAsText,因为加密数据存储为 Base64 编码字符串(OpenSSL 格式),可以直接传递给CryptoJS.AES.decrypt。该字符串被隐式转换为CipherParams 对象(here,或者,可以显式传递CipherParams 对象)。 CryptoJS.AES.decrypt 将解密后的数据返回为WordArray,因此需要转换为类型化数组或ArrayBuffer 来创建blob。为此,可以使用以下函数:

    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 编码(因为二进制数据不包含可打印字符),here。

    解密方式的变化:

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

    【讨论】:

    • 感谢您的回答,我得到了我想要的答案!
    • 集成 API WebCrypto 的加密/解密不适用于大文件(取决于客户端计算机,但通常约为 1GB)。我们应该对文件进行分块,然后处理加密的块。 CryptoJS 会单独管理吗?它适用于大文件吗?
    • S. Progressive Ciphering。如果您还有其他问题,请提出新问题。
    猜你喜欢
    • 2021-11-04
    • 2020-11-24
    • 2014-09-12
    • 2014-02-06
    • 2013-08-16
    • 1970-01-01
    • 2018-06-26
    • 2022-09-27
    相关资源
    最近更新 更多