2017-06-01 164 views
0

我使用forge作爲加密庫(用於執行加密的gulp腳本)和前端(在瀏覽器內解密的情況下)。使用Javascript:AES加密緩慢

該電腦是一個i5-6200U瓦特/ 16GB RAM,並需要大約10秒的對稱加密。或解密15MB json文件。

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

我當然錯過了一些關鍵元素(緩衝區或......我在域中缺乏的經驗可能會錯過)。在下面的代碼中有明顯的錯誤嗎?感謝您的關注。

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

請注意,JavaScript在加密時極其緩慢。沒有本地32/64位計算的腳本語言對很多事情都有好處,但快速加密不是其中之一。如果可能的話,你應該跳出JS並調用一個本地函數。 –

+0

嗨@MaartenBodewes,感謝您的信息。好的,很高興知道。我試圖使用「window.crypto.subtle」對象。似乎它使用「本機」功能。你對這個工具有什麼意見嗎? – Jem

+1

我對該公用程序沒有任何意見,甚至從未使用它。 –

回答

0

不知道,到目前爲止,但在benchmark page運行的代碼,如下圖所示,運行方式較慢: (source available here

/*** 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,在我的吞嚥腳本中有相同的數據:165ms。不知道爲什麼,到目前爲止。

4

鍛造至少0.7.1使用字符串作爲其內部緩衝區實現。 (此代碼在現代緩衝API和未來的Forge版本之前會使用更新的API。)處理大量輸入時會產生一些後果。由於輸出字符串緩衝區在處理過程中會變得更大,因此內部JavaScript虛擬機可能只會進行字符串處理。避免這種情況的一種方法是使用Forge API的流式傳輸功能,以便字符串緩衝區操作使用較大的數據塊。可以使用update()以塊形式處理輸入,並在此過程中手動構建輸出。使用getBytes()獲得輸出塊將清除輸出緩衝區,並允許Forge內部操作更高效。用這些塊構建自己的輸出不會有同樣的性能影響。

A test被編寫爲檢查通過單個update()調用,許多update()調用和本機節點API來解密大緩衝區。隨着輸入大小從1M增加到20M,單個update()調用vs本地節點API的放緩速度將從〜8倍增加到50倍!但是如果您使用流處理,放緩速度可能只有〜4.6x,並且不會明顯依賴於輸入大小!對於你的15M輸入大小,這相當於〜0.75s和〜10.31s。對於比較節點是〜0.15s和WebCrypto API可能類似。 (從i7-4790K開始計時)

A test也被寫入以查看塊大小如何影響結果。在處理大量輸入時,使用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改進可能會大大改變性能結果。

+1

我強烈建議通過setImmediate /如上建議的setTimeout。即使使用本地Web Crypto API,您也可能發現大型文件的總加密/解密時間遠遠超過16毫秒 - 這是您最想花費在單個主要JS線程上的時間最多的時間如果您想要流暢的用戶界面,請關閉事件循環。不幸的是,Web Crypto API目前還沒有流媒體API。 – dlongley