簡體   English   中英

Javascript:AES加密速度很慢

[英]Javascript: AES encryption is slow

我將forge用作gulp腳本(執行加密)和前端腳本(發生瀏覽器內解密)的加密庫。

這台計算機是i5-6200U,帶有16GB內存,兩個對稱編碼要求約10秒。 或解密15MB的json文件。

我真正的問題是解密時間對於用戶來說太長了(在該系統上加載和解密多個文件需要30多秒)。

我當然缺少一些關鍵要素(緩沖區或...我在該領域缺乏的經驗可能會錯過)。 以下代碼中是否明顯存在錯誤? 感謝您的關注。

  1. 獲取數據

     function logic(url){ return new Promise( (resolve, reject) => { var xhr = new XMLHttpRequest(); xhr.onload = function (event) { resolve(xhr.response); }; xhr.onreject = function (err) { reject(err); } xhr.open('GET', url); xhr.send(); }); } 
  2. 解密數據

     load('data/dicom.json').then( bytes => { const tIn = new Date().getTime(); const forge = getForge(); const pwd = "aStringPassword"; const iv = getInitVector(); const salt = getSalt(); const key = forge.pkcs5.pbkdf2(pwd, salt, 100, 16); var decipher = forge.cipher.createDecipher('AES-CBC', key); decipher.start({iv: iv}); decipher.update(forge.util.createBuffer(bytez)); decipher.finish(); const clear = decipher.output.getBytes(); const tOut = new Date().getTime(); console.log(`decrypted in ${(tOut - tIn) / 1000 }s`); // 10s for 15MB json file return clear ; }); 

Forge至少有0.7.1的內部緩沖區實現使用字符串。 (此代碼早於現代緩沖區API,並且將來的Forge版本將使用較新的API。)這在處理大輸入時會產生一些后果。 隨着處理過程中輸出字符串緩沖區變大,內部JavaScript VM可能會減慢僅進行字符串處理的速度。 避免這種情況的一種方法是使用Forge API的流功能,以便字符串緩沖區操作使用較大的數據塊。 可以使用update()對輸入進行分塊處理,並在此過程中手動構建輸出。 使用getBytes()獲取輸出塊將清除輸出緩沖區,並使Forge內部更有效地運行。 用這些塊構建自己的輸出不會對性能產生相同的影響。

編寫了一個測試 ,以檢查通過單個update()調用,許多update()調用以及本機節點API對大型緩沖區進行解密的情況。 隨着輸入大小從1M增加到20M,與本地節點API相比,單個update()調用的速度降低了約8倍,超過了50倍! 但是,如果您使用流式處理,則速度減慢可能僅為〜4.6倍,並且不明顯取決於輸入大小! 對於您的15M輸入大小,這相當於〜0.75s與〜10.31s。 為了進行比較,節點約為0.15s,WebCrypto API可能相似。 (從i7-4790K開始計時)

還編寫了一個測試 ,以查看塊大小如何影響結果。 當處理大量輸入時,似乎使用node.js達到約64k最佳。 這可能會有所不同,具體取決於JavaScript VM和其他因素。 關鍵要點在於,使用任何塊大小(甚至1M!)的流技術都可以避免輸入大小增加導致線性緩沖區速度降低的問題。

一個具有改進且更穩定的性能的示例:

const decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
const length = bytes.length;
const chunkSize = 1024 * 64;
let index = 0;
let clear = '';
do {
  clear += decipher.output.getBytes();
  const buf = forge.util.createBuffer(bytes.substr(index, chunkSize));
  decipher.update(buf);
  index += chunkSize;
} while(index < length);
const result = decipher.finish();
assert(result);
clear += decipher.output.getBytes();

該代碼的第二個問題是您要避免在主JS線程上執行CPU密集型代碼。 流API將允許您通過setImmediate() (如果有)或setTimeout()運行每個update()調用。 這將允許用戶在進行處理時與瀏覽器進行交互。 如果您還可以流式傳輸輸入數據,則可以在數據通過網絡傳輸時開始進行處理。 更新原始代碼以執行此操作的操作留給讀者。 在這種情況下,較小的塊大小可能有助於UI交互性。

最后應該注意的是,本地API可能總是比Forge更高。 當前的WebCrypto API不提供流API,但其性能可能足夠高,因此在這種情況下可能不會成為問題。 值得一試,看看最有效的方法。

還要注意,您應該檢查decipher.finish()返回值。

對於較大的輸入,加密具有相同的緩沖區問題,並且可以使用與上面的代碼相同的模式。

對於以后閱讀這些內容的人:較新的Web API和Forge的改進可能會大大改變性能結果。

到目前為止,還沒有任何線索,但是代碼在基准頁上運行,如下所示,運行速度更慢:( 可在此處獲得源代碼

   /*** encrypt */  
   var input = forge.util.createBuffer("plaintext");  
   var cipher = forge.aes.startEncrypting(key, iv);  
   cipher.update(input);  
   var status = cipher.finish();  
   var ciphertext = cipher.output.data;  

通過網頁測試運行:35ms,在我的gulp腳本中,數據相同:165ms。 到目前為止,不知道為什么。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM