15
7-1 我們都有使用密碼保護私密資料的經驗,甚至可以說是習慣。我們往往不希望 無關的人窺探我們的隱私,從孩童時代就知道用所謂的密碼日記本記錄自己的一 些隱私。密碼日記本就是一個帶鎖的日記本。不管是讀日記還是寫日記都離不開這 個密碼(鎖)。 上述情形就好比我們應用黑箱(black box),需要讀寫操作,需要同一套密鑰。 寫入操作伴隨加密,讀取操作也伴隨解密。加密和解密操作使用同一套密鑰,這就 是對稱式加密演算法的核心。對稱式加密演算法在 Java 實作層面上大同小異,本文 挑選了幾個具有代表性的演算法做相應實作。 7.1 對稱式加密演算法簡述 對稱式加密演算法是當今應用範圍最廣,使用頻率最高的加密演算法。它不僅 應用於軟體行業,在硬體行業同樣流行。各種基礎設施但凡涉及安全需求,都會優 先考慮對稱式加密演算法。 7.1.1 對稱式加密演算法的由來 對於大多數對稱式加密演算法而言,解密演算法是加密演算法的逆運算,加密 密鑰和解密密鑰相同。如果我們把 Base64 演算法改良,將其字元映射表作為密鑰 保存,就可以把這個改良後的 Base64 演算法作為一種對稱式加密演算法來看。當 然,加密演算法這樣改良後的安全強度還遠遠不夠,但足以讓我們認識對稱式加密 演算法的特點。 對稱式加密演算法易於理解,便於實作,根據加密方式又分為密碼和區塊加 密,其區塊加密工作模式又可分為 ECBCBCCFBOFB CTR 等,密鑰長度 決定了加密演算法的安全性。有關對稱式加密演算法相關理論知識,請閱讀第 2 相關內容。 對稱式加密演算法發展至今已相當完備。以 DES 演算法為例,由於密鑰長度 的不足夠,衍生出了 DESede 演算法(也稱為 TripleDES 3DES 演算法,翻譯成

對稱式加密演算法簡述 - epaper.gotop.com.twepaper.gotop.com.tw/pdf/ACL031600.pdf · 決定了加密演算法的安全性。有關對稱式加密演算法相關理論知識,請閱讀第2

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

7-1

我們都有使用密碼保護私密資料的經驗,甚至可以說是習慣。我們往往不希望

無關的人窺探我們的隱私,從孩童時代就知道用所謂的“密碼日記本”記錄自己的一些隱私。密碼日記本就是一個帶鎖的日記本。不管是讀日記還是寫日記都離不開這

個密碼(鎖)。

上述情形就好比我們應用黑箱(black box),需要讀寫操作,需要同一套密鑰。寫入操作伴隨加密,讀取操作也伴隨解密。加密和解密操作使用同一套密鑰,這就

是對稱式加密演算法的核心。對稱式加密演算法在 Java實作層面上大同小異,本文挑選了幾個具有代表性的演算法做相應實作。

7.1 對稱式加密演算法簡述 對稱式加密演算法是當今應用範圍最廣,使用頻率最高的加密演算法。它不僅

應用於軟體行業,在硬體行業同樣流行。各種基礎設施但凡涉及安全需求,都會優

先考慮對稱式加密演算法。

7.1.1 對稱式加密演算法的由來

對於大多數對稱式加密演算法而言,解密演算法是加密演算法的逆運算,加密

密鑰和解密密鑰相同。如果我們把 Base64 演算法改良,將其字元映射表作為密鑰保存,就可以把這個改良後的 Base64 演算法作為一種對稱式加密演算法來看。當然,加密演算法這樣改良後的安全強度還遠遠不夠,但足以讓我們認識對稱式加密

演算法的特點。

對稱式加密演算法易於理解,便於實作,根據加密方式又分為密碼和區塊加

密,其區塊加密工作模式又可分為 ECB、CBC、CFB、OFB和 CTR等,密鑰長度決定了加密演算法的安全性。有關對稱式加密演算法相關理論知識,請閱讀第 2章相關內容。

對稱式加密演算法發展至今已相當完備。以 DES 演算法為例,由於密鑰長度的不足夠,衍生出了 DESede演算法(也稱為 TripleDES或 3DES演算法,翻譯成

7-2

中文是“三重 DES”演算法)。為了替代 DES 演算法又有了 AES(Rijndael)演算法。

此外,還有 RC 系列演算法,包含 RC2、RC4 以及針對 32 位元/64 位元計算機設計

的 RC5 演算法(細分為 RC5-32 和 RC5-64,分別對應 32 位元和 64 位元計算機)。

除了上述演算法,我們還常常會用到 Blowfish、Twofish、Serpent、IDEA 和 PBE等對稱式加密演算法。

7.1.2 對稱式加密演算法的家譜

目前已知的可透過 Java 語言實作的對稱式加密演算法大約有 20 多種,Java 6 僅

僅提供了部分演算法實作,如 DES、DESede、AES、Blowfish,以及 RC2 和 RC4 演

算法等。其他演算法(如 IDEA 演算法)需要透過第三方加密軟體套件 Bouncy Castle提供實作。

在對稱式加密演算法中,DES 演算法最具有代表性,堪稱典範;DESede 是 DES演算法的變種;AES 演算法則作為 DES 演算法的替代者;而 IDEA 演算法作為一

種強加密演算法,成為電子郵件加密軟體 PGP(Pretty Good Privacy)的核心演算法

之一。

在 Java 實作層面上,DES、DESede、AES 和 IDEA 這 4 種演算法略有不同。DES和 DESede 演算法在使用密鑰材料(Key material)還原密鑰時,建議使用各自相應的

密鑰材料實作類別(DES 演算法對應 DESKeySpec 類別,DESede 演算法對應

DESedeKeySpec 類別)完成相應轉換操作。AES 演算法在使用密鑰材料還原密鑰時,

則需要使用一般密鑰材料實作類別(SecretKeySpec 類別)完成相應轉換操作。其他

對稱式加密演算法可參照該方式實作,如 RC2、RC4、Blowfish 以及 IDEA 等演算法

均可參照 AES 演算法實作方式做相應實作。IDEA 演算法實作 Java 6 未能提供,需

要依賴第三方加密套件 Bouncy Castle 提供支援,其他由 Bouncy Castle 提供支援的對

稱式加密演算法可參照該演算法實作方式做相應實作。

7.2 資料加密標準—DES DES 演算法和 DESede 演算法統稱 DES 系列演算法。 DES 演算法是對稱式加

密演算法領域中的典型演算法,為後續對稱式加密演算法的發展奠定了堅實的基

礎。 DESede 演算法基於 DES 演算法進行三重迭代,增加其演算法安全性。經過一

番篩選,Rijndeal 演算法最終成為了 AES 演算法。這期間,對稱式加密演算法發展

迅速,與 Rijndeal 演算法競爭的演算法包括 Blowfish、Serpent 等。 IDEA 演算法也

源於增加演算法的安全性替代 DES 演算法,諸多對稱式加密演算法的發展均源於

DES 演算法的研究而來。

7-3

7.2.1 簡述

1973年,美國國家標準局(NBS,National Bureau of Standards)(即現在的美國國家標準技術研究所(National Institute of Standards and Technology,NIST))徵求國家密碼標準方案,IBM公司提交了自己研製的演算法(Luciffer演算法,於 1971年末提出)。 1977年 7月 15日,該演算法被正式採納,作為美國聯邦訊息處理標準生效,並很快應用到國際商用資料加密領域,成為標準,即資料加密標準(Data Encryption Standard,DES),DES演算法由此誕生。

DES 演算法作為現代密碼學領域中第一個官方授權的加密演算法受到全球各大密碼學機構的關注。DES演算法密鑰偏短,僅有 56位元,迭代次數偏少,受到諸如差分密碼分析(Differential Cryptanalysis)和線性密碼分析(Linear Cryptanalysis)等各種攻擊威脅,安全性受到嚴重威脅。不僅如此,由於 DES 演算法具有半公開性質,被懷疑存在美國國家安全局(National Security Agency,NSA)安置的後門,受到各大密碼學機構的強烈質疑。

1998年後,實用化 DES演算法破譯機的出現徹底宣告 DES演算法已不具備安全性。 1999年 NIST頒布新標準,規定 DES演算法只能用於遺留加密系統,但不限制使用 DESede 演算法。以當今計算機技術能力,經 DES 演算法加密的資料在24 小時內可能被破解。由此,DES 演算法正式退出歷史舞台,AES 演算法成為它的替代者。即便如此,DES演算法對於密碼學領域的貢獻確實是巨大的。各種對稱式加密演算法均由研究 DES 演算法發展而來,對後續對稱式加密演算法的發展起到奠基作用。 DES演算法實作不僅遍布軟體業,甚至很多硬體晶片本身也具備 DES加密實作。同時,作為一款較易實作的加密演算法,DES演算法也成為最應學習的對稱式加密演算法,其地位堪比 C語言在計算機語言中的地位。基於 CBC工作模式的 DES演算法相關文件可參考 RFC 1829(http://www.ietf.org/rfc/rfc1829.txt)。歷經 20年發展,DES演算法不僅應用在軟體行業,成為電子商務必不可少的加密演算法,同時也逐步滲透到硬體行業。晶片的 DES 演算法生產工藝相當完備,完全可以支援底層加密需求。

7.2.2 模型分析

第 5 章曾介紹過基於 Base64 演算法的訊息交換模型,如圖 7-1 所示。對於對稱式加密演算法,其訊息模型並沒有太大的差別。基於 DES 演算法的訊息傳遞模型如圖 7-2所示。

7-4

圖 7-1 基於 Base64 演算法的訊息交換模型

圖 7-2 基於 DES 演算法的訊息傳遞模型

兩幅時序圖的主要差別在於密鑰的使用。基於 Base64 演算法的訊息傳遞模型中沒有密鑰的概念。那是因為 Base64字元映射表本身已經公開,而 Base64字元映射表本身有了密鑰的作用。訊息傳遞雙方通訊前不需要商確該密鑰,也就省去了建

構密鑰、公佈密鑰的步驟。

甲乙雙方作為訊息傳遞雙方(甲方作為發送端,乙方作為接收端),我們假定

甲乙雙方在訊息傳遞前已商定加密演算法,欲完成一次訊息傳遞需經過如下步驟:

甲方(發送端) 乙方(接收端)

1:對資料進行 Base64編碼

2:傳送編碼後的資料

3:對資料進行 Base64解碼

甲方(發送端) 乙方(接收端)

1:建構密鑰()

2:公佈密鑰()

3:使用密鑰對資料加密()

4:傳送加密資料()

5:使用密鑰對資料解密()

7-5

1. 由訊息傳遞雙方約定密鑰,這裡由甲方建構密鑰。

2. 由密鑰建構者公佈密鑰,這裡由甲方將密鑰公佈給乙方。

3. 由訊息發送端使用密鑰對資料加密,這裡由甲方對資料加密。

4. 由訊息發送端將加密資料傳送給訊息接收端,這裡由甲方將加密資料傳送給乙方。

5. 由訊息接收端使用密鑰對加密資料解密,這裡由乙方完成資料解密。

對稱式加密演算法的優點就是簡單易行,通俗易懂。對於上述單向訊息傳遞而

言,如果乙方想要恢復甲方訊息,並不需要重複步驟 1、2,僅僅須由乙方執行步驟3、4,由甲方執行步驟 5即可。

基於 DES 演算法建構的訊息傳遞模型大都按照上述模型建構,同時包括其他對稱式加密演算法,如 DESede、AES和 IDEA等。當然,並不是所有基於對稱式加密演算法的訊息傳遞模型都按此步驟建構,PBE演算法就是一個例外。我們將在後續內容中單獨詳述基於 PBE演算法的訊息傳遞模型。

7.2.3 實作

我們知道,密鑰長度與安全性成正比,但 Java 6僅支援 56位元密鑰長度,作為補充,Bouncy Castle 提供 64 位元密鑰長度支援。在此基礎上配合不同的填充方式(如 PKCS5Padding,PKCS7Padding),可顯著提高加密系統的安全性。有關DES演算法的 Java 6實作與 Bouncy Castle實作細節如表 7-1所示。

表 7-1 DES 演算法 演算法 密鑰長度 密鑰長度預設值 工作模式 填充方式 備註

56 56

ECB、CBC、PCBC、CTR、

CTS、CFB、CFB8至 CFB128、OFB、OFB8至 OFB128

NoPadding、PKCS5Padding、 ISO10126Padding

Java 6實作

DES

64 同上 同上

PKCS7Padding、 ISO10126d2Padding、

X932Padding、 ISO7816d4Padding、

ZeroBytePadding

Bouncy Castle 實作

請讀者朋友注意 DES 演算法的實作過程,其他對稱式加密演算法與該演算法實作相類似。密鑰的建構主要需要密鑰生成器(KeyGenerator)完成生成操作,如下程式碼所示:

// 實體化密鑰生成器 KeyGenerator kg = KeyGenerator.getInstance("DES"); // 初始化 kg.init(56);

7-6

// 生成秘密密鑰 SecretKey secretKey = kg.generateKey(); // 獲得密鑰的二進位編碼形式 byte[] b = secretKey.getEncoded();

位元組陣列 b就是我們需要的秘密密鑰位元組陣列形式。這便於我們將其儲存在檔案中,或以資料流的形式在網路中傳輸。把密鑰轉化為二進位位元組陣列形式

便於保存,但若我們要使用它需要將其轉換為密鑰物件,首先需要將二進位密鑰轉

換為密鑰材料物件(這裡是 DESKeySpec 物件 dks),再使用密鑰工廠(SecretKeyFactory)生成密鑰。實作程式碼如下所示:

// 實體化 DES密鑰材料 DESKeySpec dks = new DESKeySpec(b); // 實體化秘密密鑰工廠 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); // 生成秘密密鑰 SecretKey secretKey = keyFactory.generateSecret(dks);

對於 DESede 演算法,則需要相應的 DESedeKeySpec 類別替換 DESKeySpec類別來完成上述操作。得到密鑰物件後,我們就可以對資料做加密/解密處理,加密處理程式碼如下所示:

// 實體化 Cipher cipher = Cipher.getInstance("DES"); // 初始化,設置為加密模式 cipher.init(Cipher.ENCRYPT_MODE, secretKey); // 執行操作 byte[] data = cipher.doFinal(data);

上述實體化操作未指定工作模式及填充方式,我們將在後續內容中詳述。如果

將上述初始化方法(init()方法)中的模式參數由“Cipher.ENCRYPT_MODE”改為“Cipher.DECRYPT_MODE”,則可作為解密處理。Java 6提供了 DES演算法支援,但僅支援 56 位元的密鑰長度。我們知道密鑰長度與加密強度成正比。我們可以使用 Boucy Calstle提供密鑰長度,由 56位元提高至 64位元。接下來我們完成一套基於 DES演算法的密鑰建構和加密/解密操作,如程式碼清單 7-1所示。

程式碼清單 7-1 DES 演算法實作

import java.security.Key; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; /** * DES安全編碼套件 * @author 梁棟 * @version 1.0 */

7-7

public abstract class DESCoder { /** * 密鑰演算法<br> * Java 6 只支援 56位元密鑰<br> * Bouncy Castle 支援 64位元密鑰 */ public static final String KEY_ALGORITHM = "DES"; /** * 加密/解密演算法/ 工作模式/ 填充方式 */ public static final String CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding"; /** * 轉換密鑰 * @param key 二進位密鑰 * @return Key 密鑰 * @throws Exception */ private static Key toKey(byte[] key) throws Exception { // 實體化 DES密鑰材料 DESKeySpec dks = new DESKeySpec(key); // 實體化秘密密鑰工廠 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM); // 生成秘密密鑰 SecretKey secretKey = keyFactory.generateSecret(dks); return secretKey; } /** * 解密 * @param data 待解密資料 * @param key 密鑰 * @return byte[] 解密資料 * @throws Exception */ public static byte[] decrypt(byte[] data, byte[] key) throws Exception { // 還原密鑰 Key k = toKey(key); // 實體化 Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 初始化,設置為解密模式 cipher.init(Cipher.DECRYPT_MODE, k); // 執行操作 return cipher.doFinal(data); } /** * 加密 * @param data 待加密資料 * @param key 密鑰 * @return byte[] 加密資料 * @throws Exception */ public static byte[] encrypt(byte[] data, byte[] key) throws Exception {

7-8

// 還原密鑰 Key k = toKey(key); // 實體化 Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 初始化,設置為加密模式 cipher.init(Cipher.ENCRYPT_MODE, k); // 執行操作 return cipher.doFinal(data); } /** * 生成密鑰<br> * Java 6 只支援 56位元密鑰<br> * Bouncy Castle 支援 64位元密鑰<br> * @return byte[] 二進位密鑰 * @throws Exception */ public static byte[] initKey() throws Exception { /* * 實體化密鑰生成器 * 若要使用 64位元密鑰注意替換 * 將下述程式碼中的 * KeyGenerator.getInstance(CIPHER_ALGORITHM); * 替換為 * KeyGenerator.getInstance(CIPHER_ALGORITHM, "BC"); */ KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM); /* * 初始化密鑰生成器 * 若要使用 64位元密鑰注意替換 * 將下述程式碼 kg.init(56); * 替換為 kg.init(64); */ kg.init(56); // 生成秘密密鑰 SecretKey secretKey = kg.generateKey(); // 獲得密鑰的二進位編碼形式 return secretKey.getEncoded(); } }

請讀者朋友注意上述程式碼中的生成密鑰方法(initKey()),如果我們初始化密鑰生成器時按如下方式實作,將獲得一個預設長度的密鑰:

kg.init(new SecureRandom());

Java 6僅僅提供了 56位元長度的密鑰,因此上述方法將產生一個 56位元長度的密鑰。若我們想要建構一個 64位元密鑰的 DES演算法,則需要按如下程式碼做替換實作:

KeyGenerator kg = KeyGenerator.getInstance(CIPHER_ALGORITHM, "BC");

7-9

在上述程式碼中,“BC”是 Boucy Calstle 安全提供者的縮寫。當然,你也可以使用如下方式替代上述程式碼:

import java.security.Security; import org.bouncycastle.jce.provider.BouncyCastleProvider; // 省略 // 加入 BouncyCastleProvider支援 Security.addProvider(new BouncyCastleProvider()); KeyGenerator kg = KeyGenerator.getInstance(CIPHER_ALGORITHM);

完成密鑰生成器實體化操作後,需要注意密鑰生成器初始化操作,如以下程式

碼所示:

kg.init(64);

按上述程式碼實作方式,我們即可獲得 Boucy Calstle安全提供者提供的 64位元的 DES演算法密鑰。

注意 密鑰生成和加密/解密所使用的演算法很可能是不同的。在本文的 DES演算法實作中,密鑰演算法(變數 KEY_ALGORITHM)是“DES”,而加密/解密演算法(變數“CIPHER_ALGORITHM”)是“DES/ECB/PKCS5Padding”。這裡的加密/解密演算法中除了包含密鑰演算法(DES)外,還包含了工作模式(ECB)和填充方式(PKCS5Padding)。如果密鑰演算法與加密/解密演算法一致,則按預設工作模式和填充方式實作。

在實際應用中,密文通常以二進位資料傳輸/儲存,而密鑰通常會被轉換為可見字元儲存。如使用 Base64 編碼或十六進位編碼將不可見的二進位密鑰轉換為可見字元。當然,這裡需要注意一點,若使用 Base64 編碼,則編碼後的訊息將是原始訊息長度的 4/3倍(相關原理請閱讀第 5章)。為了便於示範,作者將密文和密鑰均使用 Base64編碼形式展示(我們使用 Commons Codec完成 Base64演算法實作,相關內容請參考第 5章)。測試範例實作如程式碼清單 7-2所示。

程式碼清單 7-2 DES 演算法實作測試範例

import static org.junit.Assert.*; import org.apache.commons.codec.binary.Base64; import org.junit.Test; /** * DES安全編碼套件校驗 * @author 梁棟 * @version 1.0 */ public class DESCoderTest { /** * 測試 * @throws Exception */ @Test

7-10

public final void test() throws Exception { String inputStr = "DES"; byte[] inputData = inputStr.getBytes(); System.err.println("原文:\t" + inputStr); // 初始化密鑰 byte[] key = DESCoder.initKey(); System.err.println("密鑰:\t" + Base64.encodeBase64String(key)); // 加密 inputData = DESCoder.encrypt(inputData, key); System.err.println("加密後:\t" + Base64.encodeBase64String(inputData)); // 解密 byte[] outputData = DESCoder.decrypt(inputData, key); String outputStr = new String(outputData); System.err.println("解密後:\t" + outputStr); // 校驗 assertEquals(inputStr, outputStr); } }

我們來觀察控制台的輸出訊息,如下所示:

原文: DES

密鑰: qA2oZBaKNOk=

加密後: QwCjNM5G8KM=

解密後: DES

加密/解密操作順利完成!

這樣的密鑰短小精悍,便於記憶!作者與合作公司在建構加密通訊模組時,通

常會指定這樣便於書寫的密鑰,並將其書寫在合約書上傳送給對方。這就是為什麼

要將密鑰儲存為 Base64編碼或十六進位編碼的原因。

隨著計算機的發展,密鑰長度僅有 56位元的 DES演算法顯得越來越不安全,雖然透過 Bouncy Castle 可將密鑰長度增至 64 位元,提高了其安全強度,但 DES演算法在設計上的漏洞已經不能透過單純地增加密鑰長度來彌補,這引發了對稱式

加密演算法研發競賽!DESede 和 AES 演算法正是這場競賽中具有代表性的演算法。

7.3 三重 DES—DESede 作為 DES演算法的一種改良,DESede演算法針對其密鑰長度偏短和迭代次數

偏少等問題做了相應改進,提高了安全強度。但這仍不是終點,DESede 演算法的出現僅為 DES演算法的改良提供了一種參考。 DESede演算法處理速度較慢,密鑰計算時間較長,加密效率不高等問題使得對稱式加密演算法的發展仍不容樂觀。

7-11

7.3.1 簡述

DES 演算法被廣大密碼學機構質疑的原因主要在於 DES 演算法的半公開性,違反了柯克霍夫原則,各大密碼學結構懷疑美國國家安全局在未公開的演算法實作

內安置後門。DES演算法有 3點安全隱患:密鑰太短、迭代偏少和半公開性。這使得淘汰 DES 演算法成為一種必然,但要淘汰 DES 演算法必須找到合適的替代方案。針對密鑰太短和迭代偏少問題,有人提出了多重 DES的方式來克服這些缺陷。比較典型的有雙重 DES(2DES)、三重 DES(3DES)和四重 DES(4DES)等幾種形式,但在實際應用中一般採用 3DES 方案,它還有兩個別名 Triple DES 和DESede。在 Java 中,我們通常稱其為 DESede 演算法。當然,其他兩種名稱在使用時同樣可以獲得支援。DESede演算法將密鑰長度增至 112位元或 168位元,抵抗窮舉攻擊(exhaustion attack)的能力顯著增強,但核心仍是 DES演算法,雖然透過增加迭代次數提高了安全性,但同時也造成處理速度較慢,密鑰計算時間加長,加

密效率不高的問題。

7.3.2 實作

對 DES演算法實作有一定了解後,DESede演算法的實作就容易許多。除了將密鑰材料實作類別由 DESKeySpec類別轉換為 DESedeKeySpec類別外,DESede演算法與 DES演算法實作的主要差別在於演算法、密鑰長度兩個方面:

演算法:自然不用說,這是基本差別,只是 DESede還有很多別名,如 TripleDES和 3DES指的都是 DESede演算法。

密鑰長度:Java 6提供的 DES演算法實作支援 56位元密鑰長度,加上 Bouncy Castle相應實作可以支援到 64位元密鑰長度。 Java 6提供的 DESede演算法實作所支援的密鑰長度支援為 112位元和 168位元,透過 Bouncy Castle相應實作可支援密鑰長度為 128 位元和 192 位元。 DESede 演算法密鑰長度恰恰是DES演算法密鑰長度的 2倍或 3倍。

有關 DESede演算法的 Java 6實作與 Bouncy Castle實作細節如表 7-2所示。

表 7-2 DESede 演算法 演算法 密鑰長度 密鑰長度預設值 工作模式 填充方式 備註

112、168 168

ECB、CBC、PCBC、CTR、

CTS、CFB、CFB8至 CFB128、OFB、OFB8至 OFB128

NoPadding、PKCS5Padding、 ISO10126Padding

Java 6 實作

DES(TDES,3DES)

128、192 同上 同上

PKCS7Padding、 ISO10126d2Padding、

X932Padding、 ISO7816d4Padding、

ZeroBytePadding

Bouncy Castle 實作

7-12

透過 7.2節的 DES演算法實作演示,相信讀者對於如何實作 DES演算法,以及如何使用 Bouncy Castle 擴充密鑰長度已經很了解了,本文將闡述如何使用Bouncy Castle 擴充填充方式。對於 DESede 演算法的填充方式,Java 6 提供了NoPadding、PKCS5Padding和 ISO10126Padding共 3種填充方式。

作者與合作方商定加密演算法時,對方技術水準較高,要求使用 PKCS7Padding填充方式,著實令作者困擾,好在這時作者發現了 Bouncy Castle,才解決了這一技術難題。仔細研究,發現 Bouncy Castle不僅支援 PKCS7Padding這一種填充方式,還支援 ISO10126d2Padding、X932Padding、ISO7816d4Padding 和 ZeroBytePadding共 4種填充方式。對於如何使用 PKCS7Padding填充方式完成 DESede演算法建構的問題,我們在程式碼清單 7-3中詳述。

程式碼清單 7-3 DESede 演算法實作

import java.security.Key; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESedeKeySpec; /** * DESede安全編碼套件 * @author 梁棟 * @version 1.0 */ public abstract class DESedeCoder { /** * 密鑰演算法 * Java 6支援密鑰長度為 112位元和 168位元 * Bouncy Castle支援密鑰長度為 128位元和 192位元 */ public static final String KEY_ALGORITHM = "DESede"; /** * 加密/解密演算法/ 工作模式/ 填充方式 * Java 6支援 PKCS5Padding填充方式 * Bouncy Castle支援 PKCS7Padding填充方式 */ public static final String CIPHER_ALGORITHM = "DESede/ECB/PKCS5Padding"; /** * 轉換密鑰 * @param key 二進位密鑰 * @return Key 密鑰 * @throws Exception */ private static Key toKey(byte[] key) throws Exception { // 實體化 DES密鑰材料 DESedeKeySpec dks = new DESedeKeySpec(key); // 實體化秘密密鑰工廠

7-13

SecretKeyFactory keyFactory = SecretKeyFactory.getInstance (KEY_ALGORITHM); // 生成秘密密鑰 return keyFactory.generateSecret(dks); } /** * 解密 * @param data 待解密資料 * @param key 密鑰 * @return byte[] 解密資料 * @throws Exception */ public static byte[] decrypt(byte[] data, byte[] key) throws Exception { // 還原密鑰 Key k = toKey(key); /* * 實體化 * 使用 PKCS7Padding填充方式,按如下程式碼實作 * Cipher.getInstance(CIPHER_ALGORITHM, "BC"); */ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 初始化,設置為解密模式 cipher.init(Cipher.DECRYPT_MODE, k); // 執行操作 return cipher.doFinal(data); } /** * 加密 * @param data 待加密資料 * @param key 密鑰 * @return byte[] 加密資料 * @throws Exception */ public static byte[] encrypt(byte[] data, byte[] key) throws Exception { // 還原密鑰 Key k = toKey(key); /* * 實體化 * 使用 PKCS7Padding填充方式,按如下程式碼實作 * Cipher.getInstance(CIPHER_ALGORITHM, "BC"); */ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); // 初始化,設置為加密模式 cipher.init(Cipher.ENCRYPT_MODE, k); // 執行操作 return cipher.doFinal(data); } /** * 生成密鑰<br> * @return byte[] 二進位密鑰

7-14

* @throws Exception */ public static byte[] initKey() throws Exception { /* * 實體化 * 使用 128位元或 192位元長度密鑰,按如下程式碼實作 * KeyGenerator.getInstance(KEY_ALGORITHM, "BC"); */ KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM); /* * 初始化 * Java 6支援密鑰長度為 112位元和 168位元 * 若使用 128位元或 192位元長度密鑰,按如下程式碼實作 * kg.init(128); * 或 * kg.init(192); */ kg.init(168); // 生成秘密密鑰 SecretKey secretKey = kg.generateKey(); // 獲得密鑰的二進位編碼形式 return secretKey.getEncoded(); } }

上述程式碼與 DES演算法實作如出一轍。這要感謝 Sun提供的 JCE架構,它提供了統一的加密演算法呼叫模式。我們注意到,這裡的密鑰材料實作類別由

DESKeySpec類別改為 DESedeKeySpec類別,這是為 DESede演算法量身訂做的密鑰材料實作類別。除了密鑰材料實作類別的變化,還要注意密鑰長度的區別。Java 6支援 112位元和 168位元密鑰長度,注意初始化時顯式呼叫如下程式碼:

kg.init(168);

DESede 演算法密鑰生成器的密鑰長度初始化預設值為 168 位元,若使用無參初始化方法,一樣會產生一個 168位元的 DESede演算法密鑰。如果要使用 128位元或 192 位元長度的密鑰,需要在實體化密鑰生成器物件時就指定 Bouncy Castle作為該演算法的提供者。我們以初始化 192位元長度密鑰為例,按如下程式碼實作:

KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM, "BC"); kg.init(192);

上述密鑰長度初始化實作與 DES 演算法實作無差別,這裡要注意的是填充方式延伸。注意程式碼中的變數“CIPHER_ALGORITHM”,目前指定的填充方式是PKCS5Padding,若我們使用 PKCS7Padding填充方式除了對該變數做調整外,還需要調整 Cipher物件 cipher實體化程式碼,按如下方式實作:

Cipher.getInstance(CIPHER_ALGORITHM, "BC");

這樣我們就能獲得相應填充方式下的加密/解密實作了。

7-15

關於 DESede演算法相應的測試範例與 DES演算法的測試範例基本沒有差別,如程式碼清單 7-4所示。

程式碼清單 7-4 DESede 演算法實作測試範例

import static org.junit.Assert.*; import org.apache.commons.codec.binary.Base64; import org.junit.Test; /** * DESede安全編碼套件校驗 * @author 梁棟 * @version 1.0 */ public class DESedeCoderTest { /** * 測試 * @throws Exception */ @Test public final void test() throws Exception { String inputStr = "DESede"; byte[] inputData = inputStr.getBytes(); System.err.println("原文:\t" + inputStr); // 初始化密鑰 byte[] key = DESedeCoder.initKey(); System.err.println("密鑰:\t" + Base64.encodeBase64String(key)); // 加密 inputData = DESedeCoder.encrypt(inputData, key); System.err.println("加密後:\t" + Base64.encodeBase64String(inputData)); // 解密 byte[] outputData = DESedeCoder.decrypt(inputData, key); String outputStr = new String(outputData); System.err.println("解密後:\t" + outputStr); // 校驗 assertEquals(inputStr, outputStr); } }

我們可以從控制台中獲得訊息中明顯感受到密鑰長度的增加。控制台輸出訊息

如下所示:

原文: DESede

密鑰: N8jTp6RuZxkjJea2XVvquV5YegHQ31cV

加密後: touXuJw8vrc=

解密後: DESede

仔細研究過 Java API 的讀者朋友也許會對上述程式碼中的密鑰材料實作類別的變化敏感一些,Java API中僅僅提供了 DES、DESede和 PBE共 3種對稱式加密演算法密鑰材料實作類別。那麼,其他演算法如何還原密鑰呢? AES 演算法實作就是一個不錯的範例!