GitHub AES 高級數據加密標準(Advanced Encryption Standard),簡稱AES,由美國政府於1997年開始公開徵集的新的數據加密標準演算法。經過三輪篩選,美國政府最終於2000年10月2日正式宣佈選中密碼學家Joan Daemen和Vincent Rijmen提出的RI ...
AES
高級數據加密標準(Advanced Encryption Standard),簡稱AES,由美國政府於1997年開始公開徵集的新的數據加密標準演算法。經過三輪篩選,美國政府最終於2000年10月2日正式宣佈選中密碼學家Joan Daemen和Vincent Rijmen提出的RINJDAEL演算法作為AES。
RINJDAEL演算法之所以能夠最終被選為AES的原因是其安全、性能好、效率高、實用、靈活。
RINJDAEL演算法是一個數據塊長度和密鑰長度都可變的分組加密演算法,其數據塊長度和密鑰長度都可獨立地選定為大於等於128位且小於等於256位的32位的任意倍數。而美國頒佈AES時卻規定數據塊的長度為128位,密鑰的長度可分別選擇為128位、192位或256位。
RINJDAEL演算法仍然採用分組密碼的一種通用結構:對輪函數實施迭代的結構。只是輪函數結構採用的是代替/置換的網路結構(SP結構)。
數學基礎
AES演算法中的許多運算是按位元組和4位元組的字來定義的。把一個位元組看成是在有限域GF(28)上的一個元素,把一個4位元組的字看成是繫數取自GF(28),並且次數小於4次的多項式。
GF(28)上的基礎運算
一個由比特位b7b6b5b4b3b2b1b0組成的位元組B可表示成繫數為0或1的二進位多項式:b7x7+b6x6+b5x5+b4x4+b3x3+b2x2+b1x+b0。例如,位元組B=10011011與二進位多項式b(x)=x7+x4+x3+x+1相對應。
(1)在GF(28)上的加法
在GF(28)上的加法定義為二進位多項式的加法,其繫數模2相加。
e.m.
(x7+x4+x3+x+1)+(x6+x5+x3+x2+x+1)=x7+x6+x5+x4+x2
(2)在GF(28)上的乘法
在GF(28)上的乘法定義為二進位多項式的乘積,如果乘積次數大於7次則模一個次數為8的不可約二進位多項式。
e.m.對於不可約多項式m(x)=x8+x4+x3+x+1,
(x6+x4+x2+x+1)(x7+x+1)
=(x13+x11+x9+x8+x6+x5+x4+x3+1)(mod x8+x4+x3+x+1)
=x7+x6+1
(3)在GF(28)上的乘法逆
在GF(28)中,二進位多項式b(x)的乘法逆為滿足a(x)b(x)=1的二進位多項式a(x),並記為
a(x)=b-1(x)。其中,'00'的乘法逆為其本身。
可以使用擴展歐幾裡得演算法求得乘法逆,也可以直接將'00'~'FF'這128中情況全部試代求得乘法逆。
e.m.對於不可約多項式m(x)=x8+x4+x3+x+1,因為
x6+x2+x+1=(x8+x4+x3+x+1)+(x6+x4+x2+x+1)
x4=(x6+x4+x2+x+1)+(x6+x2+x+1)=(x8+x4+x3+x+1)+(x2+1)(x6+x4+x2+x+1)
x2+x+1=(x6+x2+x+1)+x2·x4=(x2+1)(x8+x4+x3+x+1)+x4(x6+x4+x2+x+1)
x=x4+(x2+x)(x2+x+1)=x6+x2+x+1=(x4+x+1)(x8+x4+x3+x+1)+(x6+x5+x2+1)(x6+x4+x2+x+1)
1=(x2+x+1)+(x+1)x=(x5+x4)(x8+x4+x3+x+1)+(x7+x5+x4+x3+x2+x+1)(x6+x4+x2+x+1)
所以
(x6+x4+x2+x+1)(x7+x5+x4+x3+x2+x+1)=1
即
(x6+x4+x2+x+1)-1=x7+x5+x4+x3+x2+x+1
(4)在GF(28)上的倍乘
在GF(28)中,倍乘函數 xtime(b(x))定義為x·b(x) (mod m(x))。即把位元組B左移一位,若結果次數大於7次,則加上不可約多項式m(x)。
e.m.對於不可約多項式m(x)=x8+x4+x3+x+1,
x(x6+x4+x2+x+1)=x7+x5+x3+x2+x
x(x7+x5+x3+x2+x)=(x8+x6+x4+x3+x2)+(x8+x4+x3+x+1)=x6+x2+x+1
GF(28)上的多項式運算
有限域GF(28)上的多項式是繫數取自GF(28)域元素的多項式。這樣,一個4位元組的字與一個次數小於4次的GF(28)上的多項式相對應。例如,字c='03010102'與多項式c(x)='03'x3+'01'x2+'01'x+'02'相對應。
(1)GF(28)上的多項式的加法
GF(28)上的多項式的加法定義為相應項繫數相加。所以,在域GF(28)上的兩個4位元組的字相加也就是按位異或。
e.m.
('03'x3+'01'x2+'01'x+'02')+('0B'x3+'0D'x2+'09'x+'0E')='08'x3+'0C'x2+'08'x+'0C'
(2)GF(28)上的多項式的乘法
GF(28)上的多項式a(x)=a3x3+a2x2+a1x+a0和b(x)=b3x3+b2x2+b1x+b0相乘,如果乘積次數超過4次,則模x4+1。即對於c(x)=a(x)b(x)=c3x3+c2x2+c1x+c0,
c0=a0b0⊕a3b1⊕a2b2⊕a1b3
c1=a1b0⊕a0b1⊕a3b2⊕a2b3
c2=a2b0⊕a1b1⊕a0b2⊕a3b3
c3=a3b0⊕a2b1⊕a1b2⊕a0b3
(3)GF(28)上的多項式的倍乘
GF(28)上的多項式b(x)=b3x3+b2x2+b1x+b0的倍乘x·b(x)=b2x3+b1x2+b0x+b3。即多項式的繫數迴圈左移一位。
AES參數
數據塊字數Nb
在AES演算法中,加解密要經過多次數據變換操作,每一次變換操作都會產生一個中間結果,這個結果稱為狀態。把狀態表示為一個4行Nb列的二維位元組數組,其中Nb為數據塊長度除以32。因為狀態數組有4行,所以狀態數組的每一列便為一個4位元組的字。
例如,對於長度為128的數據塊B15B14...B1B0,Nb=128÷32=4,即該數據塊可以表示為狀態數組
AES演算法中規定數據塊長度為128位,即Nb=128÷32=4。
密鑰字數Nk
類似地,密鑰也可以表示為4行Nk列的二維位元組數組,其中Nk為密鑰長度除以32。同樣地,密鑰數組的每一列為一個4位元組的字。
例如,對於長度為128的密鑰K15K14...K1K0,Nk=128÷32=4,即該密鑰可以表示為密鑰數組
AES演算法中規定密鑰長度為128位、192位或256位,即Nk取值為4、6或8。
迭代輪數Nr
AES演算法的迭代輪數Nr由Nb和Nk共同決定:
AES規定Nb=4,所以對應Nk取值4、6、8,Nr取值分別為10、12、14,即在Nb=4的情況下,Nr=Nk+6。
不可約多項式m(x)
在AES演算法中,不可約多項式建議為:m(x)=x8+x4+x3+x+1,其繫數的十六進位表示為m='11B'。
SP結構
AES的輪函數由以下3層組成:
1.非線性層:進行非線性S盒變換,由16個S盒並置而成,起混淆的作用;
2.線性混合層:進行行移位變換和列混合變換以確保多輪之上的高度擴散;
3.密鑰加層:進行輪密鑰加變換,將輪密鑰簡單地異或到中間狀態上。
AES演算法細節
(1)S盒變換
S盒變換是按位元組進行的代替變換,是作用在狀態中每個位元組上的一種非線性位元組變換。
加密過程
加密過程中的S盒變換按以下2步進行:
(1)把位元組的值用它的乘法逆來代替;
(2)進行如下的仿射變換:
xi'=xi⊕xi+4⊕xi+5⊕xi+6⊕xi+7
y7y6y5y4y3y2y1y0=(x7'x6'x5'x4'x3'x2'x1'x0')⊕(01100011)
加密過程的S盒表如下:
解密過程
解密過程中的S盒變換按以下2步進行:
(1)進行如下的仿射變換:
xi'=xi+2⊕xi+5⊕xi+7
y7y6y5y4y3y2y1y0=(x7'x6'x5'x4'x3'x2'x1'x0')⊕(00000101)
(2)把位元組的值用它的乘法逆來代替。
解密過程的S盒表如下:
(2)行移位變換
行移位變換是對狀態的行進行迴圈移位變換。移位值C1、C2、C3與Nb有關:
AES規定Nb=4,所以C1=1,C2=2,C3=3。
加密過程
加密過程中的行移位變換,狀態的第0行不移位,第1行迴圈左移C1位元組,第2行迴圈左移C2位元組,第3行迴圈左移C3位元組。
解密過程
解密過程中的行移位變換,狀態的第0行不移位,第1行迴圈左移Nb-C1位元組,第2行迴圈左移Nb-C2位元組,第3行迴圈左移Nb-C3位元組。
(3)列混合變換
列混合變換是對狀態的列進行混合變換。
加密過程
加密過程中的列混合變換,把狀態中的每一列看作GF(28)上的多項式,並與固定多項式c(x)='03'x3+'01'x2+'01'x+'02'相乘。
解密過程
解密過程中的列混合變換,把狀態中的每一列看作GF(28)上的多項式,並與固定多項式c(x)='0B'x3+'0D'x2+'09'x+'0E'相乘。
(4)輪密鑰加變換
輪密鑰加變換是利用輪密鑰對狀態進行模2相加的變換。輪密鑰長度等於數據塊長度。在這個操作中,輪密鑰被簡單地異或到狀態中去。
(5)輪密鑰產生演算法
輪密鑰根據輪密鑰產生演算法由主密鑰產生得到。輪密鑰產生分2步進行:密鑰擴展和輪密鑰選擇,且遵循以下原則:
1.輪密鑰的比特總數為數據塊長度與輪數加1的,即Nb(Nr+1)。
2.首先將用戶密鑰擴展為一個擴展密鑰。
3.再從擴展密鑰中選出輪密鑰:第1個輪密鑰由擴展密鑰中的前Nb個字組成,第2個輪密鑰由接下來的Nb個字組成,以此類推。
加密過程
加密過程的輪密鑰產生分以下2步進行:
1.密鑰擴展:用1個字元素的一維數組W[Nb(Nr+1)]存儲擴展密鑰。把主密鑰放在數組W最開始的Nk個字中,其他的字由它前面的字經過處理(處理過程參照代碼部分)後得到。分Nk≤6和Nk>6兩種情況進行密鑰擴展,兩種情況的密鑰擴展策略稍有不同。
2.輪密鑰選擇:輪密鑰i由輪密鑰緩衝區W[Nb*i]到W[Nb*(i+1)-1]的字組成。
解密過程
解密過程的輪密鑰產生分以下2步進行:
1.加密過程的輪密鑰產生。
2.把解密過程的列混合變換應用到除第一個和最後一個輪密鑰之外的所有輪密鑰上。
AES演算法結構
無論是加密過程還是解密過程,都由以下部分組成:
1.一個初始輪密鑰加。
2.Nr-1輪的標準輪函數(包括S盒變換、行移位、列混合、輪密鑰加)。
3.最後一輪的非標準輪函數(只包括S盒變換、行移位、輪密鑰加,不需要列混合)。
AES的安全性
AES的安全涉及策略是寬軌跡策略。寬軌跡策略是針對差分攻擊和先行攻擊提出來的。它的最大優點是可以給出演算法的最佳差分特征的概率以及最佳線性逼近的偏差界,由此可以分析演算法的抵抗差分攻擊和線性攻擊的能力。從而確保密碼演算法具有所需要的抵抗差分攻擊和線性攻擊的能力,確保密碼演算法的安全。
(1)抗攻擊能力
AES的主要密碼部件S盒和列混合都設計得相當好。其列混合的一個重要密碼學指標分支數達到了最佳值,其S盒在非線性度、自相關性、差分均勻性、代數免疫性等主要密碼學指標方面都達到相當好的水平。
然而,AES中的列混合的擴散度不夠,密鑰擴展的非線性不夠,而且缺少抵抗側通道分析的設計。這些不足之處給密碼分析提供了可乘之機。
(2)弱密鑰
AES的加解密演算法採用不同的密鑰擴展演算法,而且都使用了非線性的S盒變換,並且在擴展產生每一輪密鑰中使用不同的輪函數。這些措施使得AES不存在弱密鑰和半弱密鑰,也就是說,在AES加解密演算法中,對密鑰的選擇沒有除長度外的任何限制。
(3)適應性
AES的密鑰長度可變,因此能夠適應不同的安全應用環境。即使今後計算能力和攻擊能力提高了,只要及時提高密鑰的長度,便可獲得滿意的安全,因此密碼的安全使用壽命長。
Java實現
word類
1 public class word { 2 byte[] word; 3 4 public word(byte[] b) { 5 word = new byte[4]; 6 for (int i = 0; i < 4; i++) 7 word[i] = b[i]; 8 } 9 10 public word(word w) { 11 word = new byte[4]; 12 for (int i = 0; i < 4; i++) 13 word[i] = w.word[i]; 14 } 15 16 @Override 17 public String toString() { 18 String str = ""; 19 for (byte b : word) 20 str += Integer.toHexString((b & 0xff) + 0x100).substring(1); 21 return str; 22 } 23 24 /** 25 * 在GF(2^8)上的多項式加法 26 * @param a 27 * @param b 28 * @return 29 */ 30 static word add(word a, word b) { 31 word c = new word(new byte[4]); 32 for (int i = 0; i < 4; i++) 33 c.word[i] = add(a.word[i], b.word[i]); 34 return c; 35 } 36 37 /** 38 * 在GF(2^8)上的多項式乘法 39 * @param a 40 * @param b 41 * @return 42 */ 43 static word multiply(word a, word b) { 44 word c = new word(new byte[4]); 45 c.word[0] = add( 46 add( 47 add( 48 multiply(a.word[0], b.word[0]), multiply(a.word[3], b.word[1])), 49 multiply(a.word[2], b.word[2])), 50 multiply(a.word[1], b.word[3])); 51 c.word[1] = add( 52 add( 53 add( 54 multiply(a.word[1], b.word[0]), multiply(a.word[0], b.word[1])), 55 multiply(a.word[3], b.word[2])), 56 multiply(a.word[2], b.word[3])); 57 c.word[2] = add( 58 add( 59 add( 60 multiply(a.word[2], b.word[0]), multiply(a.word[1], b.word[1])), 61 multiply(a.word[0], b.word[2])), 62 multiply(a.word[3], b.word[3])); 63 c.word[3] = add( 64 add( 65 add( 66 multiply(a.word[3], b.word[0]), multiply(a.word[2], b.word[1])), 67 multiply(a.word[1], b.word[2])), 68 multiply(a.word[0], b.word[3])); 69 return c; 70 } 71 72 /** 73 * 在GF(2^8)上的多項式倍乘 74 * @param a 75 * @return 76 */ 77 static word xtime(word a) { 78 word b = new word(new byte[4]); 79 for (int i = 0; i < 4; i++) 80 b.word[i] = a.word[(i + 1) % 4]; 81 return b; 82 } 83 /**********************************************************************************/ 84 static int m = 0x11b; //m=100011011 85 86 /** 87 * 在GF(2^8)上的加法 88 * @param a 89 * @param b 90 * @return 91 */ 92 static byte add(byte a, byte b) { 93 return (byte) (a ^ b); 94 } 95 96 /** 97 * 在GF(2^8)上的求模 98 * @param a 99 * @param b 100 * @return 101 */ 102 static byte mod(int a, int b) { 103 String str_a = Integer.toBinaryString(a); 104 String str_b = Integer.toBinaryString(b); 105 if (str_a.length() < str_b.length()) 106 return (byte) a; 107 return mod(a ^ (b << (str_a.length() - str_b.length())), b); 108 } 109 110 /** 111 * 在GF(2^8)上的乘法 112 * @param a 113 * @param b 114 * @return 115 */ 116 static byte multiply(byte a, byte b) { 117 int op = a & 0xff; 118 char[] c = Integer.toBinaryString((b & 0xff) + 0x100).substring(1).toCharArray(); 119 int r = 0; 120 for (int i = 0; i < c.length; i++) 121 if (c[i] == '1') 122 r ^= op << (7 - i); 123 return mod(r, m); 124 } 125 126 /** 127 * 在GF(2^8)上的乘法逆 128 * @param a 129 * @return 130 */ 131 static byte inverse(byte a) { 132 if (a == 0) return 0; 133 byte b = -128; 134 while (mod(multiply(a, b), m) != 1) 135 b++; 136 return b; 137 } 138 139 /** 140 * 在GF(2^8)上的倍乘 141 * @param a 142 * @return 143 */ 144 static byte xtime(byte a) { 145 int r = (a & 0xff) << 1; 146 if (r > 127) 147 return mod(r, m); 148 return (byte) r; 149 } 150 }
仿射變換
1 /** 2 * 仿射變換 3 * @param b 4 * @param sign C:加密 D:解密 5 * @return 6 */ 7 static byte AffineTransformation(byte b, char sign) { 8 byte[] x = Integer.toBinaryString((b & 0xff) + 0x100).substring(1).getBytes(); 9 for (int i = 0; i < x.length; i++) x[i] -= '0'; 10 if (sign == 'C') { 11 byte[] x_ = new byte[8]; 12 byte b_ = 0; 13 for (int i = 0; i < 8; i++) { 14 x_[i] = (byte) (x[i] ^ x[(i + 1) % 8] ^ x[(i + 2) % 8] ^ x[(i + 3) % 8] ^ x[(i + 4) % 8]); 15 b_ += x_[i] * Math.pow(2, 7 - i); 16 } 17 return (byte) (b_ ^ 0x63); 18 } else { 19 byte[] x_ = new byte[8]; 20 byte b_ = 0; 21 for (int i = 0; i < 8; i++) { 22 x_[i] = (byte) (x[(i + 1) % 8] ^ x[(i + 3) % 8] ^ x[(i + 6) % 8]); 23 b_ += x_[i] * Math.pow(2, 7 - i); 24 } 25 return (byte) (b_ ^ 0x05); 26 } 27 }
輪密鑰產生演算法
1 /** 2 * 加密密鑰擴展 3 * @param CipherKey 4 * @return 5 */ 6 static word[][] KeyExpansion(word[] CipherKey) { 7 word[] W = new word[Nb * (Nr + 1)]; 8 //密鑰擴展 9 word Temp; 10 if (Nk <= 6) { 11 for (int i = 0; i < Nk; i++) 12 W[i] = CipherKey[i]; 13 for (int i = Nk; i < W.length; i++) { 14 Temp = new word(W[i - 1]); 15 if (i % Nk ==0) 16 Temp = word.add(SubByte(Rotl(Temp)), Rcon(i / Nk)); 17 W[i] = word.add(W[i - Nk], Temp); 18 } 19 } else { 20 for (int i = 0; i < Nk; i++) 21 W[i] = CipherKey[i]; 22 for (int i = Nk; i < W.length; i++) { 23 Temp = new word(W[i - 1]); 24 if (i % Nk ==0) 25 Temp = word.add(SubByte(Rotl(Temp)), Rcon(i / Nk)); 26 else if (i % Nk == 4) 27 Temp = SubByte(Temp); 28 W[i] = word.add(W[i - Nk], Temp); 29 } 30 } 31 //輪密鑰選擇 32 word[][] RoundKey = new word[Nr + 1][Nb]; 33 for (int i = 0; i < Nr + 1; i++) 34 for (int j = 0; j < Nb; j++) 35 RoundKey[i][j] = W[Nb * i + j]; 36 return RoundKey; 37 }
1 static word SubByte(word a) { 2 word w = new word(a); 3 for (int i = 0; i < 4; i++) { 4 //乘法逆代替 5 w.word[i] = word.inverse(w.word[i]); 6 //仿射變換 7 w.word[i] = AffineTransformation(w.word[i], 'C'); 8 } 9 return w; 10 } 11 12 static word Rotl(word a) { 13 word w = new word(a); 14 byte b = w.word[0]; 15 for (int i = 0; i < 3; i++) 16 w.word[i] = w.word[i + 1]; 17 w.word[3] = b; 18 return w; 19 } 20 21 static word Rcon(int n) { 22 word Rcon = new word(new byte[4]); 23 byte RC = 1; 24 for (int i = 1; i < n; i++) 25 RC = word.xtime(RC); 26 Rcon.word[0] = RC; 27 return Rcon; 28 }
1 /** 2 * 解密密鑰擴展 3 * @param CipherKey 4 * @return 5 */ 6 static word[][] InvKeyExpansion(word[] CipherKey) { 7 word[][] InvRoundKey = KeyExpansion(CipherKey); 8 for (int i = 1; i < Nr; i++) 9 InvRoundKey[i] = InvMixColumn(InvRoundKey[i]); 10 return InvRoundKey; 11 }
S盒變換
1 /** 2 * S盒變換 3 * @param state 4 * @return 5 */ 6 static word[] ByteSub(word[] state) { 7 for (int i = 0; i < Nb; i++) 8 for (int j = 0; j < 4; j++) { 9 //乘法逆代替 10 state[i].word[j] = word.inverse(state[i].word[j]); 11 //仿射變換 12 state[i].word[j] = AffineTransformation(state[i].word[j], 'C'); 13 } 14 return state; 15 }
1 /** 2 * S盒逆變換 3 * @param state 4 * @return 5 */ 6 static word[] InvByteSub(word[] state) { 7 for (int i = 0; i < Nb; i++) 8 for (int j = 0; j < 4; j++) { 9 //仿射變換 10 state[i].word[j] = AffineTransformation(state[i].word[j], 'D'); 11 //乘法逆代替 12 state[i].word[j] = word.inverse(state[i].word[j]); 13 } 14 return state; 15 }
行移位變換
1 /** 2 * 行移位變換 3 * @param state 4 * @return 5 */ 6 static word[] ShiftRow(word[] state) { 7 byte[][] b = new byte[4][Nb]; 8 for (