2013年7月11日 星期四

在 Java 使用加密演算法(六):KeyGenerator、Cipher、工作模式與填充方式之間的關係

記錄一下有關 KeyGenerator(在這個主題中,可以替換為 KeyPairGenerator 或 KeyFactory) 與 Cipher 之間的關係。

keyGenerator 是用來產生 Key 的產生器,取得 KeyGenerator 的實體必須透過 getInstance() 方法,同時需要輸入使用的演算法的名稱。
Cipher 則是用來進行加密、解密的工具,取得 Cipher 的實體也必須透過 getInstance() 方法,同時輸入要使用的演算法名稱。

到這裡看起來沒有什麼問題,不過實際去查詢演算法名稱時,會發現有很多奇怪的字串!
根據書上 [1] 寫的,演算法名稱的基本組成是:演算法/工作模式/填充方式
其中工作模式實際上是該演算法的不同實作的方法,而對應不同的工作模式,就會出現演算法能加密的長度各有不同
例如加密的資料必須是 16 bits 或者是 16 bits 的倍數等等。
但資料不會永遠都這麼剛好是演算法需要的那個長度,因此就會需要透過特定的填充方式,把不夠的 bits 補足 [2]。

舉例來說,Sun JCE 原生支援 AES 演算法,並提供了 CBC 工作模式與 PKCS5Padding 的填充方法
因此要使用這個方法做加解密,就必須在初始化 Cipher 時提供演算法的完整名稱為「AES/CBC/PKCS5Padding」。
不過話說回來,在產生金鑰時使用的 KeyGenerator 也需要提供演算法名稱,如果是產生 AES 演算法的金鑰,演算法名稱是一樣的嗎?
答案是:不一樣
我想是因為 KeyFactory 並沒有涉及到加解密的問題,因此並不需要告知工作模式跟填充方法
也因此,初始化 KeyFactory 和 Cipher 時,各自該給的名稱應該如下:
// Generate secret key using AES algorithm.
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
Key key = keyGen.generateKey();

// Cipher a file using AES/CBC/PKCS5Padding algorithm.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
OutputStream cos = new CipherOutputStream(new FileOutputStream("/"), cipher);
上面的範例程式碼可以看出,產生金鑰時輸入的是 AES,但加密和解密時輸入的則是 AES/CBC/PKCS5Padding。

如果兩邊想要混用的話,可以同時在 KeyGenerator 和 Cipher 都使用 "AES"
對 Cipher 來說就只是沒有特別指定要使用的工作模式和填充方法,它會使用預設設定;
但如果同時在 KeyGenerator 和 Cipher 都使用 "AES/CBC/PKCS5Padding"
則在 KeyGenerator 處會拋出 NoSuchAlgorithmException。

參考資料:
1、JAVA加密與解密的藝術
2、用过AES加密吗,为啥加密后文件会变长?

沒有留言: