2014-06-09 17 views
5

我需要將加密數據從Java客戶端發送到C#服務器。現在我正在學習如何使用AES(需求)來加密數據。在此之後接受的答案android encryption/decryption with AES我做了以下內容:創建AES密鑰比種子更好的方法SecureRandom

byte[] keyStart = "qweroiwejrwoejlsifeoisrn".getBytes(); // Random character string 

byte[] toEncrypt = myMessageString.getBytes(); 

keyGen = KeyGenerator.getInstance("AES"); 
sr = SecureRandom.getInstance("SHA1PRNG"); 
sr.setSeed(keyStart); 
keyGen.init(128, sr); 
SecretKey secretKey = keyGen.generateKey(); 
byte[] secretKeyByte = secretKey.getEncoded(); 

SecretKeySpec skeySpec = new SecretKeySpec(secretKeyByte, "AES"); 
Cipher cipher = Cipher.getInstance("AES"); 
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); 
cipher.doFinal(toEncrypt); 

由於該算法採用了SecureRandom使用keyStart我不知道這是否可以在C#,甚至在其他Java程序進行解碼,而SecureRandom

只要知道keyStart的值或者我使用SecureRandom這個加密/解密工作,我仍然需要傳遞別的東西來解密嗎?

此外,有沒有更好的方法來做到這一點,或者這是一個很好?

回答

15

沒有,整個想法,你應該使用SecureRandom從靜態數據密鑰推導是相當糟糕:

  1. SecureRandom的主要功能是生成隨機值,它不應該被作爲發電機使用對於關鍵流;
  2. SecureRandom,當用"SHA1PRNG"實例化時沒有實現明確定義的算法,並且算法實際上已知會發生更改,即使從一個Sun JDK到另一個也是如此;
  3. Oracle提供的執行"SHA1PRNG"使用初始種子爲只有種子,其他可能只是加入種子到隨機池。

使用"SHA1PRNG"作爲密鑰派生函數已知會在多個版本的Android上產生問題,並且可能會在任何其他Java RE上失敗。


那麼你應該怎麼做呢?

  1. 使用new SecureRandom()甚至更​​好,KeyGenerator產生真正的隨機密鑰,沒有播種隨機數生成器,如果你需要一個全新的隨機密鑰;
  2. 直接向SecretKeySpec提供已知密鑰的byte[],或使用十六進制解碼器從十六進制解碼(注意String實例很難從內存中刪除,因此只有在沒有其他方式時才能執行此操作)。
  3. 如果您想從密碼創建密鑰(使用比鏈接中提供的更高的迭代計數),則使用PBKDF2;
  4. 如果要從一個密鑰種子創建多個密鑰,請使用真實的基於密鑰的密鑰推導機制,例如,使用HKDF(見下文)。

如果種子由例如植物產生,選項4將是優選的。密鑰協議算法,如Diffie-Hellman或ECDH。


請注意,對於選項3,PBKDF2,明智的做法是隻保留ASCII密碼。這是由於Oracle的PBKDF2實現不使用UTF-8編碼。


至於選項4,我已經幫助與添加都好KBKDF的到Bouncy Castle libraries所以沒有必要實施KBKDF自己,如果你可以添加充氣城堡到類路徑和/或列表安裝安全提供商。目前最好的KBKDF可能是HKDF。如果無法將Bouncy Castle添加到類路徑中,那麼您可能希望將派生數據中SHA-256輸出的最左邊字節用作「窮人」KDF。