AES加密算法原理

 隨着對稱密碼的發展,DES數據加密標準算法由於密鑰長度較小(56位),已經不適應當今分佈式開放網絡對數據加密安全性的要求,因此1997年NIST公開徵集新的數據加密標準,即AES[1]。經過三輪的篩選,比利時Joan Daeman和Vincent Rijmen提交的Rijndael算法被提議爲AES的最終算法。此算法將成爲美國新的數據加密標準而被廣泛應用在各個領域中。儘管人們對AES還有不同的看法,但總體來說,AES作爲新一代的數據加密標準匯聚了強安全性、高性能、高效率、易用和靈活等優點。AES設計有三個密鑰長度:128,192,256位,相對而言,AES的128密鑰比DES的56密鑰強1021倍[2]。AES算法主要包括三個方面:輪變化、圈數和密鑰擴展。

  AES 是一個新的可以用於保護電子數據的加密算法。明確地說,AES 是一個迭代的、對稱密鑰分組的密碼,它可以使用128、192 和 256 位密鑰,並且用 128 位(16字節)分組加密和解密數據。與公共密鑰密碼使用密鑰對不同,對稱密鑰密碼使用相同的密鑰加密和解密數據。通過分組密碼返回的加密數據 的位數與輸入數據相同。迭代加密使用一個循環結構,在該循環中重複置換(permutations )和替換(substitutions)輸入數據。Figure 1 顯示了 AES 用192位密鑰對一個16位字節數據塊進行加密和解密的情形。


Figure 1 部分數據

AES算法概述

  AES 算法是基於置換和代替的。置換是數據的重新排列,而代替是用一個單元數據替換另一個。AES 使用了幾種不同的技術來實現置換和替換。爲了闡明這些技術,讓我們用 Figure 1 所示的數據討論一個具體的 AES 加密例子。下面是你要加密的128位值以及它們對應的索引數組:00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

192位密鑰的值是:

00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 170 1 2 3 4 5 6 7 8 9 10 1112 13 14 15 16 17 18 19 20 21 22 23


Figure 2 S-盒( Sbox )

當 AES 的構造函數(constructor)被調用時,用於加密方法的兩個表被初始化。第一個表是代替盒稱爲S-盒。它是一個16×16的矩陣。S-盒的前五行和前五列如 Figure 2 所示。在幕後,加密例程獲取該密鑰數組並用它來生成一個名爲w[]的密鑰調度表,Figure 3 所示。


Figure 3 密鑰調度表(Key Sched)

w[] 最初的 Nk (6) 行被作爲種子,用原始密鑰值(0x00 到0x17)。剩餘行從種子密鑰來產生。變量 Nk 代表以 32 位字爲單位的種子密鑰長度。稍後我分析 AES 實現時你將清楚地看到 w[] 是怎樣產生的。 關鍵是這裏現在有許多密鑰使用而不只是一個。這些新的密鑰被稱爲輪密鑰(round keys)以將它們與原始種子密鑰區別開來。


Figure 4 State (態)數組

  AES 加密例程開始是拷貝 16 字節的輸入數組到一個名爲  State (態)的 4×4 字節矩陣中。(參見 Figure 4)。AES 加密算法 取名爲 Cipher,它操作 State[],其過程描述的僞代碼參見Figure 5
  在規範中,加密算法實現的一個預備的處理步驟被稱爲 AddRoundKey(輪密鑰加)。AddRoundKey 用密鑰調度表中的前四行對 State 矩陣實行一個字節一個字節的異或(XOR)操作,並用輪密鑰表 w[c,r] 異或 輸入 State[r,c]。
  舉個例子,如果 State 矩陣的第一行保存的字節是{ 00, 44, 88, cc },第一列密鑰調度表是{ 00, 04, 08, 0c },那麼新的 State[0,2] 值是用 w[2,0]( 0x08 或 0x80 )異或 State[0,2](0x88)的結果:

1 0 0 0 1 0 0 00 0 0 0 1 0 0 0 XOR1 0 0 0 0 0 0 0

AES 算法的主循環對 State 矩陣執行四個不同的操作,在規範中被稱爲 SubBytes(字節替換)、ShiftRows(行位移變換)、MixColumns(列混合變換) 和 AddRoundKey。除了每次循環 AddRoundKey 都被調用並使用密鑰調度表的下面四行外,AddRoundKey 與預備處理步驟中的 AddRoundKey 相同。SubBytes 例程是一個代替操作,它將 State 矩陣中的每個字節替換成一個由 Sbox 決定的新字節。比如,如果 State[0,1]的值是 0x40 如果你想找到它的代替者,你取 State[0,1] 的值 (0x40) 並讓 x 等於左邊的數字(4)並讓 y 等於右邊的數字(0)。然後你用 x 和 y 作爲索引 進到 Sbox 表中尋找代替值,如 Figure 2 所示。
  ShiftRows 是一個置換操作,它將 State 矩陣中的字節向左旋轉。Figure 6 示範了 ShiftRows 如何操作 State[]。State 的第0行被向左旋轉0個位置,State 的第1行被向左旋轉1個位置,State 的第2行被向左旋轉2個位置,而 State 的第3行被向左旋轉3個 位置。


Figure 6 對 State 進行 ShiftRows 操作

MixColumns 是一個代替操作,它是理解 AES 算法時最具技巧(或者說是最需要動腦筋的部分)的部分。它用 State 字節列的值進行數學域加和域乘的結果代替每個字節。我將在下一節中 詳細解釋專門的域加和域乘細節。
  假設 State[0,1] 的值是0x09,並且列1上的其它值分別爲 0x60,0xe1 和 0x04,那麼State[0,1]的新值計算如下:

State[0,1] = (State[0,1] * 0x01) + (State[1,1] * 0x02) +(State[2,1] * 0x03) +(State[3,1] * 0x01)= (0x09 * 0x01) + (0x60 * 0x02) + (0xe1 * 0x03) +(0x04 * 0x01)= 0x57

此處加法和乘法是專門的數學域操作,而不是平常整數的加法和乘法。
  SubBytes、ShiftRows、MixColumns 和 AddRoundKey 四個操作在一個執行 Nr 次的循環裏被調用,Nr 爲給定密鑰大小的輪數減 1。加密算法使用的輪數要麼是10,12,要麼是14,這依賴於種子密鑰長度是128位、192 位還是 256 位。在這個例子中,因爲 Nr 等於12, 則這四個操作被調用11次。該迭代完成後,在拷貝 State 矩陣到輸出參數前,加密算法調用 SubBytes、ShiftRows 和 AddRoundKey 後結束。
  大致說來,AES 加密算法的核心有四個操作。AddRoundKey 使用從種子密鑰值中生成的輪密鑰代替 4 組字節。SubBytes 替換用一個代替表 替換單個字節。ShiftRows 通過旋轉 4字節行 的 4 組字節進行序列置換。MixColumns 用域加和域乘的組合來替換字節。

有限域GF(28)的加法和乘法

  正如你所看到的,AES 加密算法使用相當簡單明瞭的技術來代替和置換,除 MixColumns 例程以外。MixColumns 使用特殊的加法和乘法。AES 所用的加法和乘法是基於數學(譯者注:近世代數)的域論。尤其是 AES 基於有限域GF(28)。
  GF(28)由一組從 0x00 到 0xff 的256個值組成,加上加法和乘法,因此是(28)。GF代表伽羅瓦域,以發明這一理論的數學家的名字命名。GF(28) 的一個特性是一個加法或乘法的操作的結果必須是在{0x00 ... 0xff}這組數中。雖然域論是相當深奧的,但GF(28)加法的最終結果卻很簡單。GF(28) 加法就是異或(XOR)操作。
  然而,GF(28)的乘法有點繁難。正如你稍後將在 C# 實現中所看到的,AES的加密和解密例程需要知道怎樣只用七個常量 0x01、0x02、0x03、0x09、0x0b、0x0d 和 0x0e 來相乘。所以我不全面介紹GF(28)的乘法,而只是針對這七種特殊情況進行說明。
  在GF(28)中用0x01的乘法是特殊的;它相當於普通算術中用1做乘法並且結果也同樣—任何值乘0x01等於其自身。
  現在讓我們看看用0x02做乘法。和加法的情況相同,理論是深奧的,但最終結果十分簡單。只要被乘的值小於0x80,這時乘法的結果就是該值左移1比特位。如果被乘的值大於或等於0x80,這時乘法的結果就是左移1比特位再用值0x1b異或。它防止了“域溢出”並保持乘法的乘積在範圍以內。
一旦你在GF(28)中用0x02建立了加法和乘法,你就可以用任何常量去定義乘法。用0x03做乘法時,你可以將 0x03 分解爲2的冪之和。爲了用 0x03 乘以任意字節b, 因爲 0x03 = 0x02 + 0x01,因此:

b * 0x03 = b * (0x02 + 0x01) = (b * 0x02) + (b * 0x01) 這是可以行得通的,因爲你知道如何用 0x02 和 0x01 相乘和相加,同哩,用0x0d去乘以任意字節b可以這樣做: b * 0x0d = b * (0x08 + 0x04 + 0x01) = (b * 0x08) + (b * 0x04) + (b * 0x01) = (b * 0x02 * 0x02 * 0x02) + (b * 0x02 * 0x02) + (b * 0x01) 在加解密算法中,AES MixColumns 例程的其它乘法遵循大體相同的模式,如下所示: b * 0x09 = b * (0x08 + 0x01) = (b * 0x02 * 0x02 * 0x02) + (b * 0x01)b * 0x0b = b * (0x08 + 0x02 + 0x01) = (b * 0x02 * 0x02 * 0x02) + (b * 0x02) + (b * 0x01)b * 0x0e = b * (0x08 + 0x04 + 0x02) = (b * 0x02 * 0x02 * 0x02) + (b * 0x02 * 0x02) + (b * 0x02)   總之,在GF(28)中,加法是異或操作。其乘法將分解成加法和用0x02做的乘法,而用0x02做的乘法是一個有條件的左移1比特位。AES規範中包括大量 有關GF(28)操作的附加信息。

密鑰擴展

  AES加密和解密算法使用了一個由種子密鑰字節數組生成的密鑰調度表。AES規範中稱之爲密鑰擴展例程(KeyExpansion)。從本質上講,從一個原始密鑰中生成多重密鑰以代替使用單個密鑰大大增加了比特位的擴散。雖然不是無法抵禦的困難,但理解 KeyExpansion 仍是 AES 算法中的一個難點。KeyExpansion 例程高級僞代碼如下所示: KeyExpansion(byte[] key, byte[][4] w){ copy the seed key into the first rows of w for each remaining row of w { use two of the previous rows to create a new row }} “用前面兩行來產生一個新行”(“use two of the previous rows to create a new row”)的例程用到了兩個子 例程,RotWord 和 SubWord 以及一個名爲“Rcon”的常數表(作爲“輪常數”)。讓我們先來逐個看一下這三東西,然後再回到整個 KeyExpansion 的討論中來。
  RotWord 例程很簡單。它接受一個4個字節的數組並將它們向左旋轉一個位置。因爲輪調度表 w[] 有四列,RotWord 將 w[]的1行左旋。注意 KeyExpansion 使用的這個 RotWord 函數與加密算法使用的  ShiftRows (行位移變換)例程非常相似,只是它 處理的是單行密鑰調度 w[],而不是整個加密狀態表 State[]。
  SubWord 例程使用替換表 Sbox 對一給定的一行密鑰調度表 w[] 進行逐字節替換。KeyExpansion 操作中的替換實際上就像在加密算法中的 替換一樣。被代替的輸入字節被分成 (x,y) 對,它被當作進入替換表 Sbox 的索引。舉例來說,0x27的代替結果是 x=2 和 y=7,並且 Sbox[2,7] 返回 0xcc。
  KeyExpansion 例程使用一個被稱爲輪常數表的數組 Rcon[]。這些常數都是4個字節,每一個與密鑰調度表的某一行相匹配。AES 的 KeyExpansion 例程需要11個輪常數。你可以在Figure 7 中看到這些常數清單。
  每個輪常數的最左邊的字節是GF(28)域中2的冪次方。它的另一個表示方法是其每個值是前一個值乘上0x02,正如前一部分討論 GF(28) 乘法 時所描述的那樣。注意 0x80 × 0x02 = 0x1b 是 0x80 左移1個比特位後緊接着與 0x1b 進行異或,如前所述。
  現在讓我們更進一步看看 KeyExpansion 內幕中的循環。這裏所用的僞碼比以前更爲詳細,這個循環是:
for (row = Nk; row < (4 * Nr+1); ++row){ temp = w[row-1] if (row % Nk == 0) temp = SubWord(RotWord(temp)) xor Rcon[row/Nk] else if (Nk == 8 and row % Nk == 4) temp = SubWord(temp) w[row] = w[row-Nk] xor temp} 先不要去看if子句,你將看到密鑰調度表 w[] 的每一行都是前面一行與行 Nk 異或的結果(4, 6, 或 8 取決於密鑰的長度)。if條件的第一部分用 SubWord、RotWord 以及與輪常數的異或修改密鑰調度表的每個第4、第6或第8行,取決於是否密鑰的長度是128、192或256位。這個條件的第二部分將修改行 12、20 和 28 等等——對於256位密鑰而言——每 一個第8行都將添加密鑰調度額外的可變性。
  讓我們用本文開頭所舉的例子來考察 KeyExpansion 是如何開始的。種子密鑰是192-bit / 6-word 值: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17   密鑰調度字節表 w[] 的維數是 4 列並且 Nb × (Nr + 1) 等於 4 × (12 + 1),或 52 行。KeyExpansion 將種子密鑰的值拷貝到密鑰調度字節表 w[] 的第一行。因爲我的種子密鑰是 192 位(24字節),並且 w[] 表總是 4 列,在這種情況下KeyExapansion 將種子密鑰拷貝到 w[] 的前面 6 行。現在讓我們看看 KeyExapansion 例程是如何填充密鑰調度表其餘部分的。在我的例子裏,第一個被計算的行是第 6 行 ,因爲第0-5行已被種子密鑰的值填上了: temp = w[row-1] = 14 15 16 17 條件 (row % Nk == 0)爲真,因此首先 RotWord 子程序被應用:
temp = 15 16 17 14 這時 SubWord 被應用: temp = 59 47 f0 fa 用 Rcon[row / Nk] = Rcon[6 / 6] = 01 00 00 00 進行異或: temp = 58 47 f0 fa 這時用 w[row-Nk] = w[6-6] = 00 01 02 03 異或,產生了下面結果: w[6] = 58 46 f2 f9 密鑰調度表 w[] 中其餘所有行來重複這個過程本身。
  總而言之,AES 加密和解密的一個重要部分就是從最初的種子密鑰中生成多重輪密鑰。這個 KeyExapansion 算法生成一個密鑰調度並 以某種方式進行替代和置換,在這種方式中,加密和解密算法極其相似。一. AES對稱加密:
                                                      AES加密



                         分組


  二. 分組密碼的填充
                                                   分組密碼的填充 e.g.:

                                                         PKCS#5填充方式  

 三. 流密碼:

  四. 分組密碼加密中的四種模式:3.1 ECB模式
優點:1.簡單;2.有利於並行計算;3.誤差不會被傳送;缺點:1.不能隱藏明文的模式;2.可能對明文進行主動攻擊; 

3.2 CBC模式:
優點:1.不容易主動攻擊,安全性好於ECB,適合傳輸長度長的報文,是SSL、IPSec的標準。缺點:1.不利於並行計算;2.誤差傳遞;3.需要初始化向量IV 3.3 CFB模式:
 優點:
1.隱藏了明文模式;2.分組密碼轉化爲流模式;3.可以及時加密傳送小於分組的數據;缺點:1.不利於並行計算;2.誤差傳送:一個明文單元損壞影響多個單元;3.唯一的IV; 3.4 OFB模式:
 優點:
1.隱藏了明文模式;2.分組密碼轉化爲流模式;3.可以及時加密傳送小於分組的數據;缺點:1.不利於並行計算;2.對明文的主動攻擊是可能的;3.誤差傳送:一個明文單元損壞影響多個單元;

 

 

例:

============================

 

import javax.crypto.*;

import javax.crypto.spec.*;

 

 

 

public class AES {

public static String asHex(byte buf[]) {

 StringBuffer strbuf = new StringBuffer(buf.length * 2);

 int i;

 for (i = 0; i < buf.length; i++) 

 {

  if (((int) buf[i] & 0xff) < 0x10)

   strbuf.append("0");

  strbuf.append(Long.toString((int) buf[i] & 0xff, 16));

 }

 return strbuf.toString();

}

 

public static void main(String[] args) throws Exception {

 String message = "這是個加密的例子";

 System.out.println(" 原文: " + message);

 System.out.println(" 原文轉換格式顯示:" +asHex(message.getBytes()));//string=》byte=》Hex 顯示

 

 //======生成密碼

 KeyGenerator kgen = KeyGenerator.getInstance("AES");// 獲取密匙生成器

 kgen.init(128);//生成128位的AES密碼生成器  

 SecretKey skey = kgen.generateKey();// 生成密匙

 

 byte[] raw = skey.getEncoded();//編碼格式

 SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");//生成一組擴展密鑰,並放入一個數組之中

 

 Cipher cipher = Cipher.getInstance("AES");

 cipher.init(Cipher.ENCRYPT_MODE, skeySpec);//用ENCRYPT_MODE模式,用skeySpec密碼組,生成AES加密方法

 // ========加密message

 byte[] encrypted = cipher.doFinal(message.getBytes());// 加密message 

 System.out.println(" 加密後: " + encrypted);//打印密文

 System.out.println(" 密文轉換格式後:"+asHex(encrypted));//把密文轉換成16進制格式

 

 

 //=======解密

 cipher.init(Cipher.DECRYPT_MODE, skeySpec);

 byte[] original = cipher.doFinal(encrypted);// 解密

 String originalString = new String(original, "UTF8");// 重新顯示明文

 System.out.println(" 解密後:" + originalString);

 System.out.println(" 解密出的消息轉換格式顯示:" +asHex(original));//byte型原文 轉換成16進制型字符 現實

}

}

 

==================================

運行結果:

 原文: 這是個加密的例子

 原文轉換格式顯示:e8bf99e698afe4b8aae58aa0e5af86e79a84e4be8be5ad90

 加密後: [B@7e80fa6f

 密文轉換格式後:6ab7212486090091888c15d30d65a362c9abd56be3e34541b513cd80a6716099

 解密後:這是個加密的例子

 解密出的消息轉換格式顯示:e8bf99e698afe4b8aae58aa0e5af86e79a84e4be8be5ad90

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章