JavaScript 使用Crypto.js 进行AES加密文件
最近刚好遇见需要将文件加密上传的需求,自己也在这里踩了很多坑,被卡了很久,网上也很少有详细介绍对文件进行AES加密的文章,一般都只是对简单的字符串进行加密的,今天就写一篇文章记录一下在这里遇到的问题和最后的解决方案。
Crypto.js库的介绍
Crypto.js库是使用比较广泛的一个加密库,提供了MD5、Hmac、AES、RSA等加密算法,用法也比较简单,所以选用的是这个库来进行加密操作。
运行下面的代码进行Crypto的安装
npm install crypto-js
Crypto.js 加密和解密
这里简单介绍一下AES加密算法,AES加密时需要传入Key和Iv,其中Key的长度分为128、192和256位,使用不同长度的Key进行加密,所得到的密文也是完全不同的。AES加密模式分为CBC、ECB、CTR、OCF、CFB这五种,填充模式有NoPadding、PKCS#7、PKCS#5、ZerosPadding、ISO 10126等,选择不同的加密和填充模式,得到的密文也是不同的。这里就不再对AES加密算法详细的展开介绍了,如果想更详细的了解,可以去看看专门讲解AES的文章。
1.读取并转换数据
首先是使用<input type="file"/>获取File对象,然后使用FileReader读取文件的二进制数据,最后再将读取到的ArrayBuffer转换为Crypto.js定义的WordBuffer进行加密就可以了。
<input type="file" onChange={this.handleFileChanged}/>
handleFileChanged = e => {
const { files } = e.target;
const reader = new FileReader();
reader.readAsArrayBuffer(files[0]);
reader.onload = event => {
const arrayBuffer = event.target.result;
const wordBuffer = ArrayBufferToWordArray(arrayBuffer);
const AESKey = '30980f98296b77f00a55f3c92b35322d898ae2ffcdb906de40336d2cf3d556a0';
const AESIv = 'e5889166bb98ba01e1a6bc9b32dbf3e6';
const encData = AESEncData(wordBuffer, AESKey, AESIv);
const fileBlob = new Blob([encData], { type: 'application/txt' });
uploadFile(fileBlob, files[0].name);
}
}
这里之所以会涉及到ArrayBuffer转WordBuffer是因为,Crypto.js在使用AES加密是支持对string以及WordBuffer进行加密,如果直接使用string进行加密的话,后续还涉及到一些编码的问题,处理起来会相对复杂一些,所以就将二进制数据转为WordBuffer后再进行AES加密。
2.加密和解密
在进行AES加密和解密时有以下几点需要额外注意一下:
- 要确保在加密和解密时使用的Key和Iv是一致的,如果调用 CryptoJS.AES.encrypt 时未传递Iv进来的话,会随机生成一个Iv,这样就会导致解密的时候无法正确的解出来。
- 最好确保你的Key是128、192或是256位的,如果Key是字符串类型,在传入加密函数前要先转为二进制类型的数据。
- 加密端和解密端在选择AES加密模式和填充模式时要保持一致,这里使用的是CBC和Pkcs7。
export const ArrayBufferToWordArray = arrayBuffer => {
const u8 = new Uint8Array(arrayBuffer, 0, arrayBuffer.byteLength);
const len = u8.length;
const words = [];
for (let i = 0; i < len; i += 1) {
words[i >>> 2] |= (u8[i] & 0xff) << (24 - (i % 4) * 8);
}
return CryptoJS.lib.WordArray.create(words, len);
}
export const WordArrayToArrayBuffer = wordArray => {
const { words } = wordArray;
const { sigBytes } = wordArray;
const u8 = new Uint8Array(sigBytes);
for (let i = 0; i < sigBytes; i += 1) {
const byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
u8[i] = byte;
}
return u8;
}
export const AESEncData = (data, key, iv) => { // 这里的data是WordBuffer类型的数据
const bKey = CryptoJS.enc.Hex.parse(key);
// const bKey = CryptoJS.enc.Utf8.parse(key);
const bIv = CryptoJS.enc.Hex.parse(iv);
// const bIv = CryptoJS.enc.Utf8.parse(iv);
const encrypt = CryptoJS.AES.encrypt(data, bKey, { iv: bIv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
const arrayBuffer = WordArrayToArrayBuffer(encrypt.ciphertext);
return arrayBuffer;
}
export const AESDecData = (data, key, iv) => { // 这里的data是WordBuffer类型的数据
const bKey = CryptoJS.enc.Hex.parse(key);
// const bKey = CryptoJS.enc.Utf8.parse(key);
const bIv = CryptoJS.enc.Hex.parse(iv);
// const bIv = CryptoJS.enc.Utf8.parse(iv);
const decrypt = CryptoJS.AES.decrypt({ ciphertext: data }, bKey, { iv: bIv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
const arrayBuffer = WordArrayToArrayBuffer(decrypt);
return arrayBuffer;
}
结束语
至此AES加密文件的叙述就完毕了,如果本篇文章有描述错误的地方,请大家多多指正,感谢。