2016-04-25 115 views
2

我正在用Java加密文件,需要在客戶端解密它。 這是服務器端代碼CryptoJS解密(AES)來自Java的文件字節陣列

Key secretKey = new SecretKeySpec("mysecretmysecret".getBytes(), "AES"); 
Cipher cipher = Cipher.getInstance("AES"); 
cipher.init(Cipher.ENCRYPT_MODE, secretKey); 
byte[] outputBytes = cipher.doFinal(read(sampleFile)); 
return outputBytes; 

在客戶端我用Ajax請求來獲取文件,並使用CryptoJS AES:

var xhr = new XMLHttpRequest(); 
xhr.open('GET', 'file', true); 
xhr.responseType = 'arraybuffer'; 

xhr.onload = function (e) { 
     var encryptedData = this.response; 
     var decrypted = CryptoJS.AES.decrypt(encryptedData, "mysecretmysecret"); 
     console.log(decrypted); 
}; 
xhr.send(); 

但是,這並不解密文件。我得到這個打印爲在控制檯解密值:

W…y.init {words: Array[0], sigBytes: 0} 

我也試圖轉換arraybuffer到WordArray建議here,但仍是同樣的結果。 如果有人能指引我正確的方向,並告訴我我做錯了什麼,我會感到非常高興。

編輯1: 我已經解決了這個問題。我使用的代碼作爲答案發布。

+0

「mysecretmysecret」可能不是那種祕密,如果它坐在客戶端源代碼中。 –

+0

@AlexK。這只是一個POC。尚未成爲真實的代碼。 – Sandeep

+0

不要使用字符串作爲鍵,使用字節數組。從字符串到字節數組的轉換是不明確的(BoM在開始?\ 0終止符?等等)。特別是當在系統之間傳輸加密數據時,不會**依賴於系統默認值。顯式設置模式,IV/Nonce等等。你在這方面做得很少,並且依靠系統中的默認設置是相同的。任何不匹配的默認值都會導致問題。 – rossum

回答

1

讓我們回顧一下,在Java中你使用

  • AES,
  • ECB(沒有規定,但最常見的默認值; 這是不安全的!)
  • PKCS#7填充(不指定但通常是默認值;它與PKCS#5填充相同),以及作爲16個字節(取決於默認系統編碼)的密鑰的由16個字符組成的16位密碼的密碼。

如果密鑰作爲字符串傳遞給CryptoJS,則必須使用OpenSSL的EVP_BytesToKey和單輪MD5從假定密碼派生密鑰。由於您的密文未以OpenSSL兼容格式進行編碼,因此會失敗。事情是,你不需要那個。

下面的代碼將解密是從Java正確傳來的密文,但它不是很安全:

var passwordWords = CryptoJS.enc.Utf8.parse("mysecretmysecret"); 
var decrypted = CryptoJS.AES.decrypt({ 
    ciphertext: CryptoJS.lib.WordArray.create(encryptedData) // or use some encoding 
}, passwordWords, { 
    mode: CryptoJS.mode.ECB 
}); 
console.log(decrypted.toString(CryptoJS.enc.Utf8)); // in case the plaintext is text 

不包含在基本彙總ECB模式,所以你將不得不包括JavaScript file在您的頁面之後的主CryptoJS文件。
此外,默認情況下,CryptoJS不處理ArrayBuffer。您需要包含shim(來源:this answer)。


問題在於它的不安全性。 ECB模式非常不安全。

  • 切勿使用ECB mode。它是確定性的,因此不具有語義安全性。您至少應該使用像CBCCTR這樣的隨機模式。 IV /隨機數不是祕密的,所以你可以將它與密文一起發送。常用的方法是將它放在密文前面。

  • 最好是驗證您的密文,以便像padding oracle attack這樣的攻擊是不可能的。這可以通過驗證模式(如GCM或EAX)或encrypt-then-MAC方案完成。

  • 可以從密碼派生密鑰,但應該使用適當的方案,例如PBKDF2。 Java和CryptoJS都支持這些。

+0

謝謝@Artjom。這只是一個POC,我需要用盡可能少的代碼來解決這個問題。當然,我知道歐洲央行是最不安全的,但我會再次考慮到歐洲央行的加息。 – Sandeep

0

所以我終於解決了這個問題。感謝Artjom指出正確的方向。 我已將Java代碼更改爲使用帶有PKCS5Padding的CBC。

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
SecretKeySpec myKey = new SecretKeySpec("mysecretmysecret".getBytes(), "AES"); 
IvParameterSpec IVKey = new IvParameterSpec("mysecretmysecret".getBytes()); 
cipher.init(Cipher.ENCRYPT_MODE, myKey, IVKey); 
byte[] outputBytes = cipher.doFinal(read(sampleFile)); 
return outputBytes; 

而且我的JavaScript是這樣的:

​​

decrypted是WordArray。