簡介
前世今生
AES全稱Advanced Encryption Standard,也就是高級加密標准。
在DES的安全性被發現存在明顯缺陷后,亟需有另一種算法來替代DES。DES的56bit的密鑰長度太小,雖然三重DES解決了密鑰長度的問題,但是三重DES還是存在一些明顯的缺陷。1997年4月15日,NIST發起征集AES算法的活動,目的是確定一個非保密的,公開披露的,全球免費使用的加密算法,來保護隱私,也能夠替代DES。其基本要求就是:執行性能比三重DES快、至少和三重DES一樣安全、數據分組長度為128bit、密鑰長度為128/192/256bit。
由比利時的Joan Daemen和Vincent Rijmen所設計的“Rijndael數據加密算法”最終獲勝。
經過時間的檢驗,至少到到目前為止,AES的安全性能是良好的。經過這么多年的分析和測試,至今沒有發現AES的明顯缺點,也沒有找到明顯的安全漏洞。如今,生活的很多領域也都用到了AES算法,如IPsec所使用的加密算法等。
一些特性
AES加密算法的分組長度只能為128bit,但是其密鑰長度是可以變動的,分別可以為128bit/196bit/256bit三種類型。而根據不同的密鑰長度,其加密輪數也會得到變化。如表所示。但目前比較廣泛使用的是128bit長度的密鑰。
AES | 密鑰長度(32位比特字) | 分組長度(32位比特字) | 加密輪數 |
---|---|---|---|
AES-128 | 4 | 4 | 10 |
AES-192 | 6 | 4 | 12 |
AES-256 | 8 | 4 | 14 |
在初始狀態會將明文和密鑰分組中的128位再分為16組,每組一個字節。
密鑰雖然只有4個字(32bit,在任何條件都認為1字
=4字節
是不嚴謹的,會根據系統位數而產生差異,計算機進行數據處理時,一次存取、加工和傳送的數據長度稱為字),但是會根據此密鑰產生額外40個字,分別用在十輪的加密過程中。
具體過程
過程總覽
如上圖所示,加密的第一輪到第九輪的函數是一樣的,都經過過字節代換、行移位、列混合、輪密鑰加的四個階段,但有兩個特殊點,第一點是在第一輪之前,先要進行一次輪密鑰加,先將明文和原始密鑰進行一次輪密鑰加的操作;第二是在第十輪時,沒有列混合這一步驟。
如果我們將明文所分成的十六個字節用以下矩陣表示,可能會好理解一點。
其次是圖中的w[]數組,也就是由原密鑰拓展而來,w[0]-w[3]為原密鑰的四個字,w[4]-w[43]則是由原密鑰拓展而來,用於十輪的加密中。如下圖所示。
字節代換
字節代換操作
這本來是一個計算的過程,但可以將字節代換的各種可能字節的變換結果排成一個表,也就是AES中的S盒,如下圖
對於一個輸入0xXY(XY分別表示一個十六進制的數,兩個組合起來表示一個字節),X為行向量,Y為列向量,查找在表中的位置。比如對於0x57,在表中查找到結果:0x5b
矩陣表示如下
字節代換逆操作
很顯然,上面那個正向操作是加密時需要用到的,那么對於解密過程,也同樣有其逆運算過程,但是也跟上面的正向操作一樣。對於各種變換操作,可以制成一個表,也就是逆S盒,如下表
行移位
對行移位,用矩陣也很好解釋。
看圖即可,可以說盡在不言中了。
對於行移位的逆變換:
行移位的逆變換也就是將狀態矩陣中的每一位執行相反的操作,也就是狀態矩陣的第一行右移零個字節,第二行右移1個字節,第三行右移2個字節,第四行右移3個字節。
列混合
列混合運算
列混合就是通過矩陣相乘來實現的,把經過移位后的矩陣與固定矩陣相乘,來得到混淆后的狀態,如圖所示
但注意,這個矩陣相乘的過程跟普通的乘法過程並不相同,其結果的第j列可以如下圖所示來計算,可以看到,矩陣運算中加法等價於兩字節異或、乘法則是GF(2^8)乘法。
一點知識補充
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
這里需要引入一點數學知識了,來描述xtime()運算
首先,要知道有限域GF(2^8)的概念。GF域的名字很好聽,叫伽羅華域
- 什么是有限域,形象地說,域有這樣一個性質:在加法和乘法上具有封閉性。也就是說對域中的元素進行加法或乘法運算后的結果仍然是域中的元素。
- 對於GF(2),比較好理解,也就是mod2之后,其值一定在[0.1]之間。
- 那么推廣到GF(p),也就是modp之后,其值一定在p[0.p-1]之間,所以p一定要素數,這個也很好理解。
- 對於一個字節有8位對吧,也就是能表示的十進制最大值為255,所以我們希望其值位於0-255之間,所以要mod256。但是顯然,256並不是一個質數,並不能得到我們想要結果。這時候如何解決呢?
- 多項式運算與素多項式,這里我們引入了多項式來表達一個字節中不同的位。對於一個字節,假設其為'b7b6b5b4b3b2b1b0',那么就可以看成多項式的系數b7x^7+b6*x^6+b5*x^5+b4*x^4+b3*x^3+b2*x^2+b1*x^1+b0。而多項式運算跟普通的加加減法乘除法還是很不一樣的,這就給了用來解決這一問題的契機。至於素多項式,就是用多項式表示的素數的意思了。那么至於為什么mod素多項式就可以表示整個數據段的原理我還是不是特別明白。
- 有限域GF(2^8),那么此時終於能夠解釋這個的完整含義了。顯然其意義在於,使8位bit具有封閉性,處於一個有限域中。那么可以確定一個素多項式x^8 + x^4 + x^3 +x +1,不止這一個,但一般用這個。
- 介紹完GF(2^8)了,下一步就是xtime運算了。xtime運算是用來干嘛的呢。我們回到剛才的多項式乘法,顯然對於多項式乘法,我們可以使用多項式直接相乘后,除以素多項式即可,也就是mod的操作。為什么乘了又除的,很顯然,在完成乘法之后,其多項式表示的值已經超過了有限域了。所以需要mod運算取余數。
- 既然講到了這里,那還是要講一下多項式除法的運算,也就是多項式中的mod運算。直接講多項式除法不知道怎么講,等會在附錄中直接上例子吧。
- xtime():說了這么多,終於說到xtime()運算了,其實說白了,xtime()運算不過是計算多項式乘法的一種技巧運算罷了。如果你自己本來就會多項式乘法運算的話是不一定需要記這個東西的。記得多了反而加重了記憶負擔,過一段時間就不明所以了。但是,顯然這是相較於真人而言的。對於計算機來說,實現多項式乘法並不方便,而使用移位和異或運算相結合的xtime()運算就可以很方便的在計算機中實現。所以,xtime()是為計算機計算多項式乘法而生的。
- 為什么xtime()運算可以簡化多項式乘法,我們看一下兩個普通的多項式乘法:
- '57'·'13'拆分成二進制的話就是01010111·00011001=01010111·(00010000+00001000+00000001)=01010111·00010000+01010111·00001000+01010111·00000001。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
終於把基礎的數學知識給補充完了,那么接下來就是具體介紹,計算機如何實現多項式乘法了。
-
顯然,任何值*0x01=自身
-
其次,任何值*0x02,也就是上面的例子中
01010111
·00000010
,我們先從它本來的應用去理解,如果把它看作多項式乘法(x^6+x^4+x^3+x^2+x^1+1)
*x
的話,x^7+x^5+x^4+x^3+x^2+x
,得到10101110
,也就是對前面的那個二進制表示進行了左移一位的操作。其實我們的xtime()運算,就是*0x02的操作。移位后還有個判斷其是否越界的過程,如果越界了直接減去素多項式即可。有人要問了,不是進行mod嗎,為什么減去素多項式m(x)就可以了,顯然因為我們只左移了一位,再大也不會大到哪里去。直接減去素多項式m(x)即可得到余數。 -
一個左移操作即可覆蓋到所有的多項式子。我們再看任何值*0x04,也就是假如說
01010111
·00000100
,顯然完成了*0x02,左移一位后,再左移一位即可。也就是對上訴*0x02得到的結果進行一個xtime()運算就可以了。所以對於對於后面的這些數,不過就是一個累計迭代的過程。 -
在上面的tip中我們也講了,任何一個數字都可以分解成上訴那種形式,也就可以很輕易地使用xtime運算來完成多項式乘法了。
那么講到這一步,列混合到底是什么才算講清楚了。
列混合逆運算
那么用於解密的時候所使用的逆運算也就如下
也是多項式乘法,用上訴教授的多項式乘法的方法計算即可。
輪密鑰加
所謂輪密鑰加,也就是將128位的輪密鑰Ki同狀態矩陣中的數據進行逐位異或操作,如下圖所示。注意,這是字逐位異或的結果,也就是S0 S1 S2 S3 組成的32位字與密鑰字W[4i]的異或運算。
輪密鑰加的逆運算同正向的輪密鑰加運算完全一致,這是因為異或的逆操作是其自身。輪密鑰加非常簡單,但卻能夠影響S數組中的每一位。
密鑰拓展
可以看到,除了跟輪密鑰運算的操作外,其余的操作基本都是些混淆運算,所以真正讓整個加密不具有線性結構的操作也就是輪密鑰加的操作。密鑰只有128bit,不可能對每一輪的運算都使用同一個密鑰對吧,所以此處要根據原密鑰,對密鑰進行混淆,進行拓展。
怎么做呢,如下圖所示
首先AES會將初始密鑰輸入到一個4*4的狀態矩陣中,每一個元素都代表一個字節。在這個矩陣中,每一列的4個字節組成一個字,並分別命名為W[0]、W[1]、W[2]、W[3]。
在此基礎上,接着就會對W數組擴充出另外的40個元素,新元素產生的遞歸方式如下(也可以看圖):
1.如果i不是4的倍數,那么第i列由如下等式確定:
W[i]=W[i-4]⨁W[i-1]
2.如果i是4的倍數,那么第i列由如下等式確定:
W[i]=W[i-4]⨁T(W[i-1])
其中T運算一個相對復雜的運算,也需要用到S盒以及另一組了輪常量Rcon[j]。
函數T由3部分組成:字循環、字節代換和輪常量異或,這3部分的作用分別如下。
a.字循環:將1個字中的4個字節循環左移1個字節。即將輸入字[b0, b1, b2, b3]變換成[b1,b2,b3,b0]。
b.字節代換:對字循環的結果使用S盒進行字節代換。
c.輪常量異或:將前兩步的結果同輪常量Rcon[j]進行異或,其中j表示輪數。
Rcon[j]具體值如下
講到這里,AES就算講得差不多了。