浏览器上进行文件 AES 加密解密

2020-02-23 19:50:23 +08:00
 xuthus

我觉得这是一个奇葩的问题,但是我现在有这样的需求,文件上传前,先在浏览器上进行 AES 加密,在文件下载时,先进行 AES 解密再存储本地。我该如何操作,我目前正在 VUE 项目中尝试这些操作,我使用https://github.com/brix/crypto-js 来进行操作,感觉它非常流行,但是发现我只能对文本文件进行处理,对于图片之类的文件我无法操作。 附上我的部分代码

Encode() {
                if (this.file === null) {
                    console.log('file not exist!')
                }
                var CryptoJS = require('crypto-js');
                this.file_mime = this.file.type;
                this.file_name = this.file.name;
                //读取本地文件
                var reader = new FileReader();
                //读取完毕后触发
                reader.onload = () => {
                    let key = '1234567887654321';

                    var encrypted = CryptoJS.AES.encrypt(reader.result,key).ciphertext.toString();

                    this.file2 = new Blob([encrypted], {type: 'application/octet-stream'});
                    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.readAsDataURL(this.file);
            },

Decode() {
                if (this.file === null) {
                    console.log('file not exist!')
                }
                var CryptoJS = require('crypto-js');
                //读取本地文件
                var reader = new FileReader();
                //读取完毕后触发
                reader.onload = () => {
                    let key = '1234567887654321';

                    var decrypted = CryptoJS.AES.decrypt(reader.result,key).toString(CryptoJS.enc.Utf8);

                    //Blob 生成
                    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.readAsText(this.file);
            }
5941 次点击
所在节点    JavaScript
23 条回复
ysc3839
2020-02-23 19:54:54 +08:00
找支持 Blob 的加密库?
also24
2020-02-23 19:58:25 +08:00
CryptoJS 可以解密 bin 的吧…

// 不信你看电子工业出版社的 pdf
xuthus
2020-02-23 20:01:44 +08:00
@ysc3839 谢谢,我找找。

@also24 主要是我 JS 水平不够,出现的问题无法解决,即使我是面向搜索引擎编程
tealover007
2020-02-23 20:05:19 +08:00
加密是一个很费 CPU 计算的操作。
加密图片的意义在什么地方,是为了防止没有权限的人获取?
换一个思路:如果远程服务器可以存放未加密的图片,那系统用权限控制就好。
估计上面的方案不是你想要的,那么
再换一个思路:利用离线 aes 加解密软件加密所需文件,把 aes 解密工具和加密后的文件上传到系统,对方下载 aes 加解密软件和文件,然后自己解密。文件的存放路径或者业务场景处理好,应该问题不大。
多了一个下载解密软件和解密的操作。
also24
2020-02-23 20:11:41 +08:00
@xuthus #3
你 decrypt 之后,为什么要 tostring ?
also24
2020-02-23 20:12:54 +08:00
另:你可能没有理解电子工业出版社的梗

你可以点击下面链接里的 『立即阅读』,它的在线阅读器里的 pdf 就是用 JS 做的 AES 解密:
https://yd.51zhy.cn/ebook/web/newBook/queryNewBookById?id=64556589
xuthus
2020-02-23 20:16:02 +08:00
@tealover007 这确实是最优方案。但是我在写我的毕业设计[信息安全专业],是一个关于网盘类型的项目。我有想使用访问控制的手段来隔离,但是被要求文件存储需要进行加密 /解密处理,这种用户自行加密解密显然不太合适。
xuthus
2020-02-23 20:17:19 +08:00
@also24 CryptoJS.AES.encrypt(reader.result,key) 这个对象无法写入 Blob,所以我就把他 tostring 了。
also24
2020-02-23 20:17:51 +08:00
另外,我看了下,你的 AES 似乎没有指定 mode ?

你可以需要了解一下 AES 的几种 mode
https://zh.wikipedia.org/zh-hans/%E5%88%86%E7%BB%84%E5%AF%86%E7%A0%81%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F
xuthus
2020-02-23 20:21:34 +08:00
@also24 我在正常测试时,是有指定的,CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7}) 这种,CryptoJS 有提供,当然,流处理上,我会选计算器模式 CTR。
xuthus
2020-02-23 20:26:34 +08:00
@also24 我看了电子工业出版社的 pdf,发现确实是,Key 和 FileURL 都给返回了。
also24
2020-02-23 20:35:19 +08:00
https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader

我看了一下 FileReader,在读取的时候需要指定
FileReader.readAsArrayBuffer()
FileReader.readAsBinaryString()
FileReader.readAsDataURL()

你在 encode 方法里用的是 readAsDataURL,也就是说 reader.result 里其实是文件在 base64 编码后的字符串。
而你把这个字符串直接传进了 encrypt 方法,也就是说此时你其实是在加密这个字符串。
encrypt 方法返回的 encryptedData 直接 tostring 的话,是密文 base64 后的字符串,
但你取的是 encryptedData.ciphertext.tostring(),这个实际上是密文的 hexString 字符串。

相应的,你在 Decode 方法里使用的是 readAsText,文件内容是字符串的情况下也不算太错
但是你把这串文本直接丢进 decrypt 方法就大错特错了,应当从 hexString 转回 Blob 才对


当然,以上是针对你的程序来说的。
我来写的话,应该会选择 readAsDataURL 读到 base64 编码后的文件,
然后 base64 解码后丢进 encrypt 方法,再把 encryptedData.toString() 的字符串解码后丢进输出文件。
LuRenJiasWorld
2020-02-23 20:39:40 +08:00
有个建议,试试看 wasm 的库,比如这个:
https://github.com/flash1293/aes-wasm

这些库一般都是从成熟的 C/C++库 port 过来的,缺点是一般都要传入 ArrayBuffer,处理起来费一点代码
xuthus
2020-02-23 20:42:19 +08:00
@also24 你的解读是正确的,我的思路一开始就是:先得到 base64Data->aes 加密 base64Data->存为一个文本。解密直接读取文本->aes 解密->base64Data 解码。
xuthus
2020-02-23 20:45:38 +08:00
@LuRenJiasWorld 谢谢,我去研究研究。
also24
2020-02-23 20:51:07 +08:00
总结一下,请务必分清楚几种不同的数据格式:

一个字符串,它的内容是:『 V2EX 』

对应的 ASCII 码是:
86 50 69 88

对应的十六进制分别为:
56 32 45 58

那么在 UTF8 / ANSI 编码下,按大端序书写,它的二进制( binary )内容是:
0101 0110 0011 0010 0100 0101 0101 1000

相应的 HEX 字符串就是:
0x56324558

相应的 base64 字符串是:
VjJFWA==

你的错误就在于混淆使用了 binary / b64 String / HEX String,从而导致混乱

不过确实,网上找到的很多资料都在混淆使用或者不仔细说明。
事实上,AES 的 encrypt 方法只管你输入的是 binary( byte array ) 就好了,字符串还是文件对它来说没区别。

总之,你这里只需要配合 FileReader,选择正确的编解码方式就好了。
个人不建议对 base64 String 进行加解密操作,这样会造成密文文件体积增大,徒增开销。
如果我没有理解错的话,直接使用 FileReader.readAsArrayBuffer() 应该能够避免中间 b64 编解码的开销。
xuthus
2020-02-23 20:53:44 +08:00
@also24 谢谢你的建议,我确实是混淆了类型,我去试试看!
tyx1703
2020-02-23 21:29:13 +08:00
正好前两天看过这个

我用的这个库 https://cryptojs.gitbook.io/docs/#encoders。
首先读取文件用 FileReader.readAsArrayBuffer(),然后把 ArrayBuffer 转成十六进制字符串。
再用 crypto-js 读取十六进制字符串进行加密。
加密过程结束之后,crypto-js 把结果转成十六进制字符串,然后转换成 ArrayBuffer,写入文件对象即可。
xuthus
2020-02-23 22:09:13 +08:00
@tyx1703 谢谢你的提示,我将尝试你的建议。
muzuiget
2020-02-24 00:12:11 +08:00
为何上面两人的语法像机器翻译的样子?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/646889

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX