2012-08-31 71 views
3

我使用BouncyCastle的加密/解密使用AES和PKCS5填充一些文件在CBC模式:檢查加密密鑰正確性

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC"); 

現在兩個問題:

  • 我如何檢查提供的解密數據密鑰是否正確?
  • 如何檢查加密輸入是否未觸及(例如,用戶使用HEX編輯器未更改)?

感謝

回答

1

加密不只是算法和加密密鑰,這也是很多關於 系統組織。

通常,您不能確定密鑰是否正確。任何密鑰都可以用來解密應該解密的數據,但是由其他一些機制來告訴你這個 是否是「正確的」結果。

通常,您不能確定要解密的數據是否未觸動,除非通過一些 外部檢查。這是大多數加密系統的一個特性,更改任何加密的數據將會大大改變解密的輸出,可能會變成你認爲垃圾爲 的東西。

1

您應該添加一個MAC,它首先驗證消息的完整性,然後才能解密它。 MAC的一個常見選擇是使用您喜歡的任何散列函數(例如SHA-2)的HMAC。

不要自己這樣做,使用經過驗證的密碼通常是一個好主意。 AES-GCM是常見的選擇。但是在這種情況下,你需要非常小心,不要重複使用IV。

4

您可以使用像CCM或GCM這樣的AEAD mode,來代替CBC。這些模式驗證加密的消息,所以如果使用了錯誤的密鑰或者密碼文本已被更改,您可以檢測到它。儘管如此,你將無法區分這些情況。

GCM支持Java 7的加密API,但隨Oracle的Java實現一起提供的SunJCE提供程序還不支持它。您可以通過BouncyCastle等第三方提供商獲得支持。

如果您使用其他加密服務(如數字簽名或消息認證代碼),則可以實現相同的目的。

+0

這可能是最簡單的方法。他只需要確定他永遠不會重複使用IV。在這種情況下,GCM失敗了。 – CodesInChaos

1

JCE密碼通常是非常基本的。如果您需要全面的功能保護,包括完整性和關鍵測試,則需要將它們組合在一起。和往常一樣,最好不要自己設備。所以最好選擇更高級別的格式,如PKCS7/12或PGP。

根據使用的填充符,當您嘗試使用錯誤的密鑰解密時,某些密碼會給您一個PaddingException。爲了更強的完整性檢查,我會使用一個由HMAC字節組成的填充。

JCE中包含了一個非常完整的方法,它是AESWrap算法。它需要填充數據,但會確保完整性。最好與RFC 3537中描述的長度字節結合使用。注意,這僅適用於較小數量的祕密(如對稱密鑰)。RFC3537填充限制爲255個字節。

使用密碼派生密鑰利用這一點,你可以使用這個:

char[] pass = ... // your password 
byte[] codeBytes = ... // up to 255 bytes you want to protect 

// generate wrapping key from password 
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG"); 
byte[] salt = new byte[16]; rand.nextBytes(salt); 
SecretKey kek = f.generateSecret(new PBEKeySpec(pass, salt, 1000, 128)); 
kek = new SecretKeySpec(password.getEncoded(), "AES"); // convert into AES 

// RFC3537 padding (lengthbyte) 
byte[] wrappedCodeBytes = new byte[codeBytes + 1 % 8]; 
System.arraycopy(codeBytes,0,wrappedCodeBytes,1,wrappedCodeBytes.length); 
paddedCodeBytes[0]=(byte)codeBytes.length; 
byte[] pad = new byte[paddedCodeBytes.length - codeBytes.length -1]; rand.nextBytes(pad); 
System.arraycopy(pad,0,paddedCodeBytes,codeBytes.length+1,pad.length); 
// AESWrap is WRAP_MODE:needs a SecretKey 
SecretKey paddedCodeKey = new SecretKeySpec(paddedCodeBytes, "RAW"); 

// now wrap the password with AESWrap kek is 128 bit 
Cipher c = Cipher.getInstance("AESWrap"); // default IV 
c.init(Cipher.WRAP_MODE, kek); 
byte[] result = c.warp(paddedCodeKey); 

解纏留給讀者作爲一個練習:)示例代碼使用128位密鑰長度,因爲更多的熵不能被預期無論如何從PBKDF2。

請注意,這將以高概率檢測錯誤密碼,並且一些評論家將此視爲AESWrap的弱點。

1

看一看this tutorial on BC encryption,特別是InitCiphers方法,詳細在第二碼塊指定的實際類型的密碼。

如何檢查提供的解密數據密鑰是否正確?

根據JCE Javadocs類SecretKeySpec的具體構造:

此構造方法不檢查給定的字節實際上是否指定了一個指定算法的密鑰。例如,如果算法是DES,則此構造函數不檢查密鑰長度是8個字節,也不檢查弱密鑰或半弱密鑰。爲了執行這些檢查,應該使用算法特定的密鑰規範類(在這種情況下爲DESKeySpec)。

注意Interface KeySpec列出了所有實現類,基本的驗證選項一個列表。

如何檢查加密輸入是否未觸及(例如,用戶使用HEX編輯器未更改)?

確實。這是一個很好的。 '輸入'是非常通用的。你的意思是解密的實際內容?那麼,如果它消失了,我相信它不會正確解密。那有意義嗎?你所談論的奇偶校驗位的密鑰的情況下

IFF被改變,如在Bouncy Castle FAQ(6)項所描述的,你必須做的關鍵的實際奇偶校驗。只有密鑰的前56個字節用於加密操作,最後8個字節保留用於奇偶校驗。所以,基本上,'鑰匙'的最後部分可以改變,第一部分仍然有用。要檢測奇偶校驗或密鑰是否已被更改,您將運行奇偶校驗。 我發現this little ditty做了奇偶校驗。而且,有關如何在這些鍵中設置奇偶校驗的更多信息,請參閱討論奇偶校驗設置的JDK7 Crypto Provider source for Class DESKeyGenerator by Jan Luehe(接近底部)中的註釋。

我最近和BC有過一些互動,我希望這個信息有幫助。

+1

您的大多數帖子都是特定於DES對待密鑰的特質方式。這些關鍵的奇偶校驗位比無用的更糟糕,而且它們甚至不適用於像AES這樣的體面密碼。OP需要某種形式的真實MAC。 – CodesInChaos