如果java输出的数据是使用CipherOutputStream按1024(假设)个字节加密输出的,那么如果前端也必须按照1024个字节分段进行解密,不然将会解密失败,以下是前端的具体解密逻辑:
前端依赖:
<script src="jq.js" />
<script src="crypto-js.js" />
详细逻辑:
convertWordArrayToUint8Array为将CryptoJs解密后的wordArray对象转化为保存文件可用的Uint8Array的字节流对象,下方会使用到
function convertWordArrayToUint8Array(wordArray) {
let arrayOfwords = wordArray.hasOwnProperty("words") ? wordArray. words : [];
let length = wordArray.hasOwnProperty("sigBytes") ? wordArray. sigBytes : arrayOfwords. length * 4;
let uInt8Array = new Uint8Array(length), index=o, 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 & exff;
}
return uInt8Array;
}
blobToArrayBuffer为将接口返回的密文数据Blob对象转化为ArrayBuffer对象,下方解密是将会使用到
function blobToArrayBuffer(blob) {
return new Promise((resolve, reject) => {
const file = new FileReader();
file.onload = function(result) {
resolve(file.result) ;
}
file.readAsArrayBuffer(blob);
})
}
decryptBlob为解密的具体逻辑,思路为仿照CipherOutputStream的加密逻辑,逆向回去,将流数组也按1024个字节切分,然后通过查阅cryptoJS的源码,模拟调用,先将分割好的流数组,转化为ArrayBuffer对象,再通过ArrayBuffer对象创建出cryptoJS解密可用的WordArray对象,最后不断通过process方法,分段传入解密,结束时调用finalize()方法,需要注意的是,finalize()也会返回解密数据的,记得拼接上去。
几个参数说明一下,blockSize对应的是CipherOutputStream每次加密输入的流长度,blob为接口返回的密文流数据,key是加密的密钥,iv则是偏移量。
let blockSize = 1024;
async function decryptBlob(blob, key, iv) {
let blobSize = blob.size;
let max = Math.floor(blobSize / blockSize);
const aesDecryptor = CryptoJS.algo.AES.createDecryptor(CryptoJS.enc.Utf8.parse(key),{
iv: CryptoJS.enc.Utf8.parse(iv);
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
let fullWordArray = undefined;
for( let i = 0; i <= max; i++ ){
let sliceStart = i*blockSize;
let sliceLength = i === max ? blobSize : (i+1)*blockSize;
let blobSlice = blob.slice(sliceStart, sliceLength);
const arrayBuffer = await blobToArrayBuffer(blobSlice);
const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer)
if(!fullWordArray){
fullWordArray = aesDecryptor.process(wordArray);
}else{
fullWordArray.concat(aesDecryptor.process(wordArray));
}
}
if(!fullwordArray){
fullWordArray = aesDecryptor.finalize();
}else{
fullWordArray.concat(aesDecryptor.finalize());
}
let fileDec = new Blob([convertWordArrayToUint8Array(fullWordArray)]);
let url = window.URL.createObjectURL(fileDec);
let filename = "test.txt";
let a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}