在 Java 環境下使用 AES 加密,在密鑰長度和字節填充方面有一些比較特殊的處理。
1. 密鑰長度問題
默認 Java 中僅支持 128 位密鑰,當使用 256 位密鑰的時候,會報告密鑰長度錯誤
Invalid AES key length
你需要下載一個支持更長密鑰的包。這個包叫做 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6,可以從這里下載,下載地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
下載之后,解壓后,可以看到其中包含兩個包:
local_policy.jar
US_export_policy.jar
看一下你的 JRE 環境,將 JRE 環境中 lib\lib\security 中的同名包替換掉。
2. Base64 問題
Apache 提供了 Base64 的實現,可以從這里下載。
下載地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
編碼
// 編碼 String asB64 = new Base64().encodeToString("some string".getBytes("utf-8")); System.out.println(asB64); // 輸出為: c29tZSBzdHJpbmc=
解碼
// 解碼 byte[] asBytes = new Base64().getDecoder().decode("c29tZSBzdHJpbmc="); System.out.println(new String(asBytes, "utf-8")); // 輸出為: some string
如果你已經使用 Java 8,那么就不需要再選用第三方的實現了,在 java.util 包中已經包含了 Base64 的處理。
編碼的方式
// 編碼 String asB64 = Base64.getEncoder().encodeToString("some string".getBytes("utf-8")); System.out.println(asB64); // 輸出為: c29tZSBzdHJpbmc=
解碼處理
// 解碼 byte[] asBytes = Base64.getDecoder().decode("c29tZSBzdHJpbmc="); System.out.println(new String(asBytes, "utf-8")); // 輸出為: some string
3. 關於 PKCS5 和 PKCS7 填充問題
PKCS #7 填充字符串由一個字節序列組成,每個字節填充該填充字節序列的長度。
假定塊長度為 8,數據長度為 9, 數據: FF FF FF FF FF FF FF FF FF PKCS7 填充: FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07
簡單地說, PKCS5, PKCS7和SSL3, 以及CMS(Cryptographic Message Syntax)
有如下相同的特點: 1)填充的字節都是一個相同的字節 2)該字節的值,就是要填充的字節的個數
如果要填充8個字節,那么填充的字節的值就是0×8; 要填充7個字節,那么填入的值就是0×7;
…
如果只填充1個字節,那么填入的值就是0×1;
這種填充方法也叫PKCS5, 恰好8個字節時還要補8個字節的0×08
正是這種即使恰好是8個字節也需要再補充字節的規定,可以讓解密的數據很確定無誤的移除多余的字節。
在PKCS# Padding中說:
- 因為恢復的明文的最后一個字節 告訴你 存在多少個填充字節, 用PKCS#5 填充 的加密方法, 即使在輸入的明文長度 恰好是 塊大小(Block Size)整數倍 , 也會增加一個完整的填充塊. 否則,恢復出來的明文的最后一個字節可能是實際的消息字節.
- 因為第1個因素限制了 使用PKCS#填充的 對稱加密算法的 輸入塊大小(Block Size, 注意不是輸入的明文的總長度 total input length), 最大只能是256個字節. 因為大多數對稱塊加密算法 通常使用8字節或者16字節的塊, 所以,這不是一個問題
- 使用ECB模式填充可能會有安全問題.
- 使用PKCS#5填充 可以很方便地檢測明文中的錯誤.
標准
PKCS #7: Cryptographic Message Syntax
在 10.3節中講到了上面提到的填充算法, 對Block Size並沒有做規定
PKCS #5: Password-Based Cryptography Specification
在6.1.1 中對 填充做了說明 但是因為該標准 只討論了 8字節(64位) 塊的加密, 對其他塊大小沒有做說明 其 填充算法跟 PKCS7是一樣的
后來 AES 等算法, 把BlockSize擴充到 16個字節
比如, Java中 Cipher.getInstance(“AES/CBC/PKCS5Padding”) 這個加密模式 跟C#中的 RijndaelManaged cipher = new RijndaelManaged(); cipher.KeySize = 128; cipher.BlockSize = 128; cipher.Mode = CipherMode.CBC; cipher.Padding = PaddingMode.PKCS7; 的加密模式是一樣的
因為AES並沒有64位的塊, 如果采用PKCS5, 那么實質上就是采用PKCS7
參考資料: