1. 程式人生 > 程式設計 >AES加密演算法的原理詳解與實現分析

AES加密演算法的原理詳解與實現分析

AES簡介

高階加密標準(AES,Advanced Encryption Standard)為最常見的對稱加密演算法(微信小程式加密傳輸就是用這個加密演算法的)。對稱加密演算法也就是加密和解密用相同的金鑰,具體的加密流程如下圖:

AES加密演算法的原理詳解與實現分析

密碼學中的高階加密標準(Advanced Encryption Standard,AES),又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。

這個標準用來替代原先的DES(Data Encryption Standard),已經被多方分析且廣為全世界所使用。經過五年的甄選流程,高階加密標準由美國國家標準與技術研究院 (NIST)於2001年11月26日釋出於FIPS PUB 197,並在2002年5月26日成為有效的標準。2006年,高階加密標準已然成為對稱金鑰加密中最流行的演算法之一 。

該演算法為比利時密碼學家Joan Daemen和Vincent Rijmen所設計,結合兩位作者的名字,以Rijdael之名命之,投稿高階加密標準的甄選流程。(Rijdael的發音近於 "Rhine doll"。)

下面簡單介紹下各個部分的作用與意義:

  • 明文P

沒有經過加密的資料。

  • 金鑰K

用來加密明文的密碼,在對稱加密演算法中,加密與解密的金鑰是相同的。金鑰為接收方與傳送方協商產生,但不可以直接在網路上傳輸,否則會導致金鑰洩漏,通常是通過非對稱加密演算法加密金鑰,然後再通過網路傳輸給對方,或者直接面對面商量金鑰。金鑰是絕對不可以洩漏的,否則會被攻擊者還原密文,竊取機密資料。

  • AES加密函式

設AES加密函式為E,則 C = E(K,P),其中P為明文,K為金鑰,C為密文。也就是說,把明文P和金鑰K作為加密函式的引數輸入,則加密函式E會輸出密文C。

  • 密文C

經加密函式處理後的資料

  • AES解密函式

設AES解密函式為D,則 P = D(K,C),其中C為密文,K為金鑰,P為明文。也就是說,把密文C和金鑰K作為解密函式的引數輸入,則解密函式會輸出明文P。

在這裡簡單介紹下對稱加密演算法與非對稱加密演算法的區別。

  • 對稱加密演算法

加密和解密用到的金鑰是相同的,這種加密方式加密速度非常快,適合經常傳送資料的場合。缺點是金鑰的傳輸比較麻煩。

  • 非對稱加密演算法

加密和解密用的金鑰是不同的,這種加密方式是用數學上的難解問題構造的,通常加密解密的速度比較慢,適合偶爾傳送資料的場合。優點是金鑰傳輸方便。常見的非對稱加密演算法為RSA、ECC和EIGamal。

實際中,一般是通過RSA加密AES的金鑰,傳輸到接收方,接收方解密得到AES金鑰,然後傳送方和接收方用AES金鑰來通訊。

本文下面AES原理的介紹參考自《現代密碼學教程》,AES的實現在介紹完原理後開始。

AES的基本結構

AES為分組密碼,分組密碼也就是把明文分成一組一組的,每組長度相等,每次加密一組資料,直到加密完整個明文。在AES標準規範中,分組長度只能是128位,也就是說,每個分組為16個位元組(每個位元組8位)。金鑰的長度可以使用128位、192位或256位。金鑰的長度不同,推薦加密輪數也不同,如下表所示:

AES 金鑰長度(32位位元字) 分組長度(32位位元字) 加密輪數
AES-128 4 4 10
AES-192 6 4 12
AES-256 8 4 14

輪數在下面介紹,這裡實現的是AES-128,也就是金鑰的長度為128位,加密輪數為10輪。
上面說到,AES的加密公式為C = E(K,P),在加密函式E中,會執行一個輪函式,並且執行10次這個輪函式,這個輪函式的前9次執行的操作是一樣的,只有第10次有所不同。也就是說,一個明文分組會被加密10輪。AES的核心就是實現一輪中的所有操作。

AES的處理單位是位元組,128位的輸入明文分組P和輸入金鑰K都被分成16個位元組,分別記為P = P0 P1 … P15 和 K = K0 K1 … K15。如,明文分組為P = abcdefghijklmnop,其中的字元a對應P0,p對應P15。一般地,明文分組用位元組為單位的正方形矩陣描述,稱為狀態矩陣。在演算法的每一輪中,狀態矩陣的內容不斷髮生變化,最後的結果作為密文輸出。該矩陣中位元組的排列順序為從上到下、從左至右依次排列,如下圖所示:

AES加密演算法的原理詳解與實現分析

現在假設明文分組P為"abcdefghijklmnop",則對應上面生成的狀態矩陣圖如下:

AES加密演算法的原理詳解與實現分析

上圖中,0x61為字元a的十六進位制表示。可以看到,明文經過AES加密後,已經面目全非。

類似地,128位金鑰也是用位元組為單位的矩陣表示,矩陣的每一列被稱為1個32位位元字。通過金鑰編排函式該金鑰矩陣被擴充套件成一個44個字組成的序列W[0],W[1],…,W[43],該序列的前4個元素W[0],W[2],W[3]是原始金鑰,用於加密運算中的初始金鑰加(下面介紹);後面40個字分為10組,每組4個字(128位元)分別用於10輪加密運算中的輪金鑰加,如下圖所示:

AES加密演算法的原理詳解與實現分析

上圖中,設K = “abcdefghijklmnop”,則K0 = a,K15 = p,W[0] = K0 K1 K2 K3 = “abcd”。

AES的整體結構如下圖所示,其中的W[0,3]是指W[0]、W[1]、W[2]和W[3]串聯組成的128位金鑰。加密的第1輪到第9輪的輪函式一樣,包括4個操作:位元組代換、行位移、列混合和輪金鑰加。最後一輪迭代不執行列混合。另外,在第一輪迭代之前,先將明文和原始金鑰進行一次異或加密操作。

AES加密演算法的原理詳解與實現分析

上圖也展示了AES解密過程,解密過程仍為10輪,每一輪的操作是加密操作的逆操作。由於AES的4個輪操作都是可逆的,因此,解密操作的一輪就是順序執行逆行移位、逆位元組代換、輪金鑰加和逆列混合。同加密操作類似,最後一輪不執行逆列混合,在第1輪解密之前,要執行1次金鑰加操作。

下面分別介紹AES中一輪的4個操作階段,這4分操作階段使輸入位得到充分的混淆。

一、位元組代換

1.位元組代換操作

AES的位元組代換其實就是一個簡單的查表操作。AES定義了一個S盒和一個逆S盒。
AES的S盒:

行/列 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 0x63 0x7c 0x77 0x7b 0xf2 0x6b 0x6f 0xc5 0x30 0x01 0x67 0x2b 0xfe 0xd7 0xab 0x76
1 0xca 0x82 0xc9 0x7d 0xfa 0x59 0x47 0xf0 0xad 0xd4 0xa2 0xaf 0x9c 0xa4 0x72 0xc0
2 0xb7 0xfd 0x93 0x26 0x36 0x3f 0xf7 0xcc 0x34 0xa5 0xe5 0xf1 0x71 0xd8 0x31 0x15
3 0x04 0xc7 0x23 0xc3 0x18 0x96 0x05 0x9a 0x07 0x12 0x80 0xe2 0xeb 0x27 0xb2 0x75
4 0x09 0x83 0x2c 0x1a 0x1b 0x6e 0x5a 0xa0 0x52 0x3b 0xd6 0xb3 0x29 0xe3 0x2f 0x84
5 0x53 0xd1 0x00 0xed 0x20 0xfc 0xb1 0x5b 0x6a 0xcb 0xbe 0x39 0x4a 0x4c 0x58 0xcf
6 0xd0 0xef 0xaa 0xfb 0x43 0x4d 0x33 0x85 0x45 0xf9 0x02 0x7f 0x50 0x3c 0x9f 0xa8
7 0x51 0xa3 0x40 0x8f 0x92 0x9d 0x38 0xf5 0xbc 0xb6 0xda 0x21 0x10 0xff 0xf3 0xd2
8 0xcd 0x0c 0x13 0xec 0x5f 0x97 0x44 0x17 0xc4 0xa7 0x7e 0x3d 0x64 0x5d 0x19 0x73
9 0x60 0x81 0x4f 0xdc 0x22 0x2a 0x90 0x88 0x46 0xee 0xb8 0x14 0xde 0x5e 0x0b 0xdb
A 0xe0 0x32 0x3a 0x0a 0x49 0x06 0x24 0x5c 0xc2 0xd3 0xac 0x62 0x91 0x95 0xe4 0x79
B 0xe7 0xc8 0x37 0x6d 0x8d 0xd5 0x4e 0xa9 0x6c 0x56 0xf4 0xea 0x65 0x7a 0xae 0x08
C 0xba 0x78 0x25 0x2e 0x1c 0xa6 0xb4 0xc6 0xe8 0xdd 0x74 0x1f 0x4b 0xbd 0x8b 0x8a
D 0x70 0x3e 0xb5 0x66 0x48 0x03 0xf6 0x0e 0x61 0x35 0x57 0xb9 0x86 0xc1 0x1d 0x9e
E 0xe1 0xf8 0x98 0x11 0x69 0xd9 0x8e 0x94 0x9b 0x1e 0x87 0xe9 0xce 0x55 0x28 0xdf
F 0x8c 0xa1 0x89 0x0d 0xbf 0xe6 0x42 0x68 0x41 0x99 0x2d 0x0f 0xb0 0x54 0xbb 0x16

狀態矩陣中的元素按照下面的方式對映為一個新的位元組:把該位元組的高4位作為行值,低4位作為列值,取出S盒或者逆S盒中對應的行的元素作為輸出。例如,加密時,輸出的位元組S1為0x12,則查S盒的第0x01行和0x02列,得到值0xc9,然後替換S1原有的0x12為0xc9。狀態矩陣經位元組代換後的圖如下:

AES加密演算法的原理詳解與實現分析

2.位元組代換逆操作

逆位元組代換也就是查逆S盒來變換,逆S盒如下:

行/列 0 1 2 3 4 5 6 7 8 9 A B C D E F
0 0x52 0x09 0x6a 0xd5 0x30 0x36 0xa5 0x38 0xbf 0x40 0xa3 0x9e 0x81 0xf3 0xd7 0xfb
1 0x7c 0xe3 0x39 0x82 0x9b 0x2f 0xff 0x87 0x34 0x8e 0x43 0x44 0xc4 0xde 0xe9 0xcb
2 0x54 0x7b 0x94 0x32 0xa6 0xc2 0x23 0x3d 0xee 0x4c 0x95 0x0b 0x42 0xfa 0xc3 0x4e
3 0x08 0x2e 0xa1 0x66 0x28 0xd9 0x24 0xb2 0x76 0x5b 0xa2 0x49 0x6d 0x8b 0xd1 0x25
4 0x72 0xf8 0xf6 0x64 0x86 0x68 0x98 0x16 0xd4 0xa4 0x5c 0xcc 0x5d 0x65 0xb6 0x92
5 0x6c 0x70 0x48 0x50 0xfd 0xed 0xb9 0xda 0x5e 0x15 0x46 0x57 0xa7 0x8d 0x9d 0x84
6 0x90 0xd8 0xab 0x00 0x8c 0xbc 0xd3 0x0a 0xf7 0xe4 0x58 0x05 0xb8 0xb3 0x45 0x06
7 0xd0 0x2c 0x1e 0x8f 0xca 0x3f 0x0f 0x02 0xc1 0xaf 0xbd 0x03 0x01 0x13 0x8a 0x6b
8 0x3a 0x91 0x11 0x41 0x4f 0x67 0xdc 0xea 0x97 0xf2 0xcf 0xce 0xf0 0xb4 0xe6 0x73
9 0x96 0xac 0x74 0x22 0xe7 0xad 0x35 0x85 0xe2 0xf9 0x37 0xe8 0x1c 0x75 0xdf 0x6e
A 0x47 0xf1 0x1a 0x71 0x1d 0x29 0xc5 0x89 0x6f 0xb7 0x62 0x0e 0xaa 0x18 0xbe 0x1b
B 0xfc 0x56 0x3e 0x4b 0xc6 0xd2 0x79 0x20 0x9a 0xdb 0xc0 0xfe 0x78 0xcd 0x5a 0xf4
C 0x1f 0xdd 0xa8 0x33 0x88 0x07 0xc7 0x31 0xb1 0x12 0x10 0x59 0x27 0x80 0xec 0x5f
D 0x60 0x51 0x7f 0xa9 0x19 0xb5 0x4a 0x0d 0x2d 0xe5 0x7a 0x9f 0x93 0xc9 0x9c 0xef
E 0xa0 0xe0 0x3b 0x4d 0xae 0x2a 0xf5 0xb0 0xc8 0xeb 0xbb 0x3c 0x83 0x53 0x99 0x61
F 0x17 0x2b 0x04 0x7e 0xba 0x77 0xd6 0x26 0xe1 0x69 0x14 0x63 0x55 0x21 0x0c 0x7d

二、行移位

1.行移位操作

行移位是一個簡單的左迴圈移位操作。當金鑰長度為128位元時,狀態矩陣的第0行左移0位元組,第1行左移1位元組,第2行左移2位元組,第3行左移3位元組,如下圖所示:

AES加密演算法的原理詳解與實現分析

2.行移位的逆變換

行移位的逆變換是將狀態矩陣中的每一行執行相反的移位操作,例如AES-128中,狀態矩陣的第0行右移0位元組,第1行右移1位元組,第2行右移2位元組,第3行右移3位元組。

三、列混合

1.列混合操作

列混合變換是通過矩陣相乘來實現的,經行移位後的狀態矩陣與固定的矩陣相乘,得到混淆後的狀態矩陣,如下圖的公式所示:

AES加密演算法的原理詳解與實現分析

狀態矩陣中的第j列(0 ≤j≤3)的列混合可以表示為下圖所示:

AES加密演算法的原理詳解與實現分析

其中,矩陣元素的乘法和加法都是定義在基於GF(2^8)上的二元運算,並不是通常意義上的乘法和加法。這裡涉及到一些資訊保安上的數學知識,不過不懂這些知識也行。其實這種二元運算的加法等價於兩個位元組的異或,乘法則複雜一點。對於一個8位的二進位制數來說,使用域上的乘法乘以(00000010)等價於左移1位(低位補0)後,再根據情況同(00011011)進行異或運算,設S1 = (a7 a6 a5 a4 a3 a2 a1 a0),剛0x02 * S1如下圖所示:

AES加密演算法的原理詳解與實現分析

也就是說,如果a7為1,則進行異或運算,否則不進行。
類似地,乘以(00000100)可以拆分成兩次乘以(00000010)的運算:

AES加密演算法的原理詳解與實現分析

乘以(0000 0011)可以拆分成先分別乘以(0000 0001)和(0000 0010),再將兩個乘積異或:

AES加密演算法的原理詳解與實現分析

因此,我們只需要實現乘以2的函式,其他數值的乘法都可以通過組合來實現。
下面舉個具體的例子,輸入的狀態矩陣如下:

C9 E5 FD 2B
7A F2 78 6E
63 9C 26 67
B0 A7 82 E5

下面,進行列混合運算:
以第一列的運算為例:

AES加密演算法的原理詳解與實現分析

其它列的計算就不列舉了,列混合後生成的新狀態矩陣如下:

D4 E7 CD 66
28 02 E5 BB
BE C6 D6 BF
22 0F DF A5

2.列混合逆運算

逆向列混合變換可由下圖的矩陣乘法定義:

AES加密演算法的原理詳解與實現分析

可以驗證,逆變換矩陣同正變換矩陣的乘積恰好為單位矩陣。

四、輪金鑰加

輪金鑰加是將128位輪金鑰Ki同狀態矩陣中的資料進行逐位異或操作,如下圖所示。其中,金鑰Ki中每個字W[4i],W[4i+1],W[4i+2],W[4i+3]為32位位元字,包含4個位元組,他們的生成演算法下面在下面介紹。輪金鑰加過程可以看成是字逐位異或的結果,也可以看成位元組級別或者位級別的操作。也就是說,可以看成S0 S1 S2 S3 組成的32位字與W[4i]的異或運算。

AES加密演算法的原理詳解與實現分析

輪金鑰加的逆運算同正向的輪金鑰加運算完全一致,這是因為異或的逆操作是其自身。輪金鑰加非常簡單,但卻能夠影響S陣列中的每一位。

金鑰擴充套件

AES首先將初始金鑰輸入到一個44的狀態矩陣中,如下圖所示。

AES加密演算法的原理詳解與實現分析

這個44矩陣的每一列的4個位元組組成一個字,矩陣4列的4個字依次命名為W[0]、W[1]、W[2]和W[3],它們構成一個以字為單位的陣列W。例如,設金鑰K為"abcdefghijklmnop",則K0 = ‘a',K1 = ‘b',K2 = ‘c',K3 = ‘d',W[0] = “abcd”。
接著,對W陣列擴充40個新列,構成總共44列的擴充套件金鑰陣列。新列以如下的遞迴方式產生:
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是一個有點複雜的函式。
函式T由3部分組成:字迴圈、位元組代換和輪常量異或,這3部分的作用分別如下。
a.字迴圈:將1個字中的4個位元組迴圈左移1個位元組。即將輸入字[b0,b1,b2,b3]變換成[b1,b3,b0]。
b.位元組代換:對字迴圈的結果使用S盒進行位元組代換。
c.輪常量異或:將前兩步的結果同輪常量Rcon[j]進行異或,其中j表示輪數。
輪常量Rcon[j]是一個字,其值見下表。

j 1 2 3 4 5
Rcon[j] 01 00 00 00 02 00 00 00 04 00 00 00 08 00 00 00 10 00 00 00
j 6 7 8 9 10
Rcon[j] 20 00 00 00 40 00 00 00 80 00 00 00 1B 00 00 00 36 00 00 00

下面舉個例子:
設初始的128位金鑰為:
3C A1 0B 21 57 F0 19 16 90 2E 13 80 AC C1 07 BD
那麼4個初始值為:
W[0] = 3C A1 0B 21
W[1] = 57 F0 19 16
W[2] = 90 2E 13 80
W[3] = AC C1 07 BD
下面求擴充套件的第1輪的子金鑰(W[4],W[5],W[6],W[7])。
由於4是4的倍數,所以:
W[4] = W[0] ⨁ T(W[3])
T(W[3])的計算步驟如下:

  1. 迴圈地將W[3]的元素移位:AC C1 07 BD變成C1 07 BD AC;
  2. 將 C1 07 BD AC 作為S盒的輸入,輸出為78 C5 7A 91;
  3. 將78 C5 7A 91與第一輪輪常量Rcon[1]進行異或運算,將得到79 C5 7A 91,因此,T(W[3])=79 C5 7A 91,故
    W[4] = 3C A1 0B 21 ⨁ 79 C5 7A 91 = 45 64 71 B0
    其餘的3個子金鑰段的計算如下:
    W[5] = W[1] ⨁ W[4] = 57 F0 19 16 ⨁ 45 64 71 B0 = 12 94 68 A6
    W[6] = W[2] ⨁ W[5] =90 2E 13 80 ⨁ 12 94 68 A6 = 82 BA 7B 26
    W[7] = W[3] ⨁ W[6] = AC C1 07 BD ⨁ 82 BA 7B 26 = 2E 7B 7C 9B
    所以,第一輪的金鑰為 45 64 71 B0 12 94 68 A6 82 BA 7B 26 2E 7B 7C 9B。

AES解密

在文章開始的圖中,有AES解密的流程圖,可以對應那個流程圖來進行解密。下面介紹的是另一種等價的解密模式,流程圖如下圖所示。這種等價的解密模式使得解密過程各個變換的使用順序同加密過程的順序一致,只是用逆變換取代原來的變換。

AES加密演算法的原理詳解與實現分析

AES原理到這裡就結束了,下面主要為AES的實現,對以上原理中的每一個小節進行實現講解,講解的時候會插入一些關鍵程式碼,完整的程式碼參見文章最後。文章最後提供兩個完整的程式,一個能在linux下面編譯執行,一個能在VC6.0下面編譯通過。

AES演算法實現

AES加密函式預覽

aes加密函式中,首先進行金鑰擴充套件,然後把128位長度的字串讀進一個4*4的整數陣列中,這個陣列就是狀態矩陣。例如,pArray[0][0] = S0,pArray[1][0] = S1,pArray[0][1] = S4。這個讀取過程是通過 convertToIntArray()函式來實現的。每個輪操作的函式都對pArray進行修改,也就是對狀態矩陣進行混淆。在執行完10輪加密後,會把pArray轉換回字串,再存入明文p的字元陣列中,所以,在加密完後,明文p的字串中的字元就是加密後的字元了。這個轉換過程是通過convertArrayToStr()函式來實現的。

/**
 * 引數 p: 明文的字串陣列。
 * 引數 plen: 明文的長度。
 * 引數 key: 金鑰的字串陣列。
 */
void aes(char *p,int plen,char *key){

  int keylen = strlen(key);
  if(plen == 0 || plen % 16 != 0) {
    printf("明文字元長度必須為16的倍數!\n");
    exit(0);
  }

  if(!checkKeyLen(keylen)) {
    printf("金鑰字元長度錯誤!長度必須為16、24和32。當前長度為%d\n",keylen);
    exit(0);
  }

  extendKey(key);//擴充套件金鑰
  int pArray[4][4];

  for(int k = 0; k < plen; k += 16) {
    convertToIntArray(p + k,pArray);

    addRoundKey(pArray,0);//一開始的輪金鑰加

    for(int i = 1; i < 10; i++){//前9輪

      subBytes(pArray);//位元組代換

      shiftRows(pArray);//行移位

      mixColumns(pArray);//列混合

      addRoundKey(pArray,i);

    }

    //第10輪
    subBytes(pArray);//位元組代換

    shiftRows(pArray);//行移位

    addRoundKey(pArray,10);

    convertArrayToStr(pArray,p + k);
  }
}

1.金鑰擴充套件的實現

在開始加密前,必須先獲得第一輪加密用到的金鑰,故先實現金鑰擴充套件
下面是金鑰擴充套件函式的實現,這個函式傳入金鑰key的字串表示,然後從字串中讀取W[0]到W[3],函式getWordFromStr()用於實現此功能。讀取後,就開始擴充套件金鑰,當i是4的倍數的時候,就會呼叫T()函式來進行擴充套件,因為T函式的行為與加密的輪數有關,故要把加密的輪數 j 作為引數傳進去。

//金鑰對應的擴充套件陣列
static int w[44];

/**
 * 擴充套件金鑰,結果是把w[44]中的每個元素初始化
 */
static void extendKey(char *key) {
  for(int i = 0; i < 4; i++)
    w[i] = getWordFromStr(key + i * 4); 

  for(int i = 4,j = 0; i < 44; i++) {
    if( i % 4 == 0) {
      w[i] = w[i - 4] ^ T(w[i - 1],j); 
      j++;//下一輪
    }else {
      w[i] = w[i - 4] ^ w[i - 1]; 
    }
  }  

}

下面是T()函式的程式碼實現,T()函式中接收兩個引數,引數num為上面傳進的W[i - 1],round為加密的輪數。首先用一個numArray儲存從32位的W[i-1]中取得4個位元組。如果W[i-1]為0x12ABCDEF,那麼numArray[0] = 0x12,numArray[1] = 0xAB。函式splitIntToArray()用於從32位整數中讀取這四個位元組,之所以這樣做是因為整數陣列比較容易操作。然後呼叫leftLoop4int()函式把numArray陣列中的4個元素迴圈左移1位。然後執行位元組代換,通過getNumFromSBox()函式來獲取S盒中相應的值來替換numArray中的值。接著通過mergeArrayToInt()函式把位元組代換後的numArray合併回32位的整數,在進行輪常量異或後返回。

/**
 * 常量輪值表
 */
static const int Rcon[10] = { 0x01000000,0x02000000,0x04000000,0x08000000,0x10000000,0x20000000,0x40000000,0x80000000,0x1b000000,0x36000000 };
/**
 * 金鑰擴充套件中的T函式
 */
static int T(int num,int round) {
  int numArray[4];
  splitIntToArray(num,numArray);
  leftLoop4int(numArray,1);//字迴圈

  //位元組代換
  for(int i = 0; i < 4; i++)
    numArray[i] = getNumFromSBox(numArray[i]);

  int result = mergeArrayToInt(numArray);
  return result ^ Rcon[round];
}

2. 位元組代換的實現

位元組代換的程式碼很簡單,就是把狀態矩陣中的每個元素傳進getNumFromSBox()函式中,然後取得前面8位中的高4位作為行值,低4位作為列值,然後返回S[row][col],這裡的S是儲存S盒的陣列。

/**
 * 根據索引,從S盒中獲得元素
 */
static int getNumFromSBox(int index) {
  int row = getLeft4Bit(index);
  int col = getRight4Bit(index);
  return S[row][col];
}

/**
 * 位元組代換
 */
static void subBytes(int array[4][4]){
  for(int i = 0; i < 4; i++)
    for(int j = 0; j < 4; j++)
      array[i][j] = getNumFromSBox(array[i][j]);
}

3.行移位的實現

行移位的時候,首先把狀態矩陣中第2,3,4行復製出來,然後對它們行進左移相應的位數,然後再複製回去狀態矩陣array中。

/**
 * 將陣列中的元素迴圈左移step位
 */
static void leftLoop4int(int array[4],int step) {
  int temp[4];
  for(int i = 0; i < 4; i++)
    temp[i] = array[i];

  int index = step % 4 == 0 ? 0 : step % 4;
  for(int i = 0; i < 4; i++){
    array[i] = temp[index];
    index++;
    index = index % 4;
  }
}

/**
 * 行移位
 */
static void shiftRows(int array[4][4]) {
  int rowTwo[4],rowThree[4],rowFour[4];
  //複製狀態矩陣的第2,3,4行
  for(int i = 0; i < 4; i++) {
    rowTwo[i] = array[1][i];
    rowThree[i] = array[2][i];
    rowFour[i] = array[3][i];
  }
  //迴圈左移相應的位數
  leftLoop4int(rowTwo,1);
  leftLoop4int(rowThree,2);
  leftLoop4int(rowFour,3);

  //把左移後的行復制回狀態矩陣中
  for(int i = 0; i < 4; i++) {
    array[1][i] = rowTwo[i];
    array[2][i] = rowThree[i];
    array[3][i] = rowFour[i];
  }
}

4.列混合的實現

列混合函式中,先把狀態矩陣初始狀態複製一份到tempArray中,然後把tempArray與colM矩陣相乘,colM為存放要乘的常數矩陣的陣列。其中的GFMul()函式定義了矩陣相乘時的乘法,加法則直接通過異或來實現。GFMul()通過呼叫乘以各個數對應的函式來實現乘法。例如,S1 * 2 剛通過呼叫GFMul2(S1)來實現。S1 * 3 剛通過GFMul3(S1)來實現。在這裡,主要實現GFMul2()函式就行了,其它的都可以通過GFMul2()的組合來實現。舉個例子吧,為計算下面這條等式,需要像下面這樣呼叫函式
ex

s = GFMul3(0xC9) ^ 0x7A ^ 0x63 ^ GFMul2(0xB0)

/**
 * 列混合要用到的矩陣
 */
static const int colM[4][4] = { 2,1,2,2 };

static int GFMul2(int s) {
  int result = s << 1;
  int a7 = result & 0x00000100;

  if(a7 != 0) {
    result = result & 0x000000ff;
    result = result ^ 0x1b;
  }

  return result;
}

static int GFMul3(int s) {
  return GFMul2(s) ^ s;
}

/**
 * GF上的二元運算
 */
static int GFMul(int n,int s) {
  int result;

  if(n == 1)
    result = s;
  else if(n == 2)
    result = GFMul2(s);
  else if(n == 3)
    result = GFMul3(s);
  else if(n == 0x9)
    result = GFMul9(s);
  else if(n == 0xb)//11
    result = GFMul11(s);
  else if(n == 0xd)//13
    result = GFMul13(s);
  else if(n == 0xe)//14
    result = GFMul14(s);

  return result;
}

/**
 * 列混合
 */
static void mixColumns(int array[4][4]) {

  int tempArray[4][4];

  for(int i = 0; i < 4; i++)
    for(int j = 0; j < 4; j++)
      tempArray[i][j] = array[i][j];

  for(int i = 0; i < 4; i++)
    for(int j = 0; j < 4; j++){
      array[i][j] = GFMul(colM[i][0],tempArray[0][j]) ^ GFMul(colM[i][1],tempArray[1][j])
        ^ GFMul(colM[i][2],tempArray[2][j]) ^ GFMul(colM[i][3],tempArray[3][j]);
    }
}

5.輪金鑰加的實現

輪金鑰加的實現很簡單,就是根據傳入的輪數來把狀態矩陣與相應的W[i]異或。

/**
 * 輪金鑰加
 */
static void addRoundKey(int array[4][4],int round) {
  int warray[4];
  for(int i = 0; i < 4; i++) {

    splitIntToArray(w[ round * 4 + i],warray);

    for(int j = 0; j < 4; j++) {
      array[j][i] = array[j][i] ^ warray[j];
    }
  }
}


AES解密函式

AES的解密函式和加密函式有點不同,可以參考上面的等價解密流程圖來理解,解密函式中呼叫的是各輪操作的逆函式。逆函式在這裡就不詳細講解了,可以參考最後的完整程式碼。

/**
 * 引數 c: 密文的字串陣列。
 * 引數 clen: 密文的長度。
 * 引數 key: 金鑰的字串陣列。
 */
void deAes(char *c,int clen,char *key) {

  int keylen = strlen(key);
  if(clen == 0 || clen % 16 != 0) {
    printf("密文字元長度必須為16的倍數!現在的長度為%d\n",clen);
    exit(0);
  }

  if(!checkKeyLen(keylen)) {
    printf("金鑰字元長度錯誤!長度必須為16、24和32。當前長度為%d\n",keylen);
    exit(0);
  }

  extendKey(key);//擴充套件金鑰
  int cArray[4][4];
  for(int k = 0; k < clen; k += 16) {
    convertToIntArray(c + k,cArray);


    addRoundKey(cArray,10);

    int wArray[4][4];
    for(int i = 9; i >= 1; i--) {
      deSubBytes(cArray);

      deShiftRows(cArray);

      deMixColumns(cArray);
      getArrayFrom4W(i,wArray);
      deMixColumns(wArray);

      addRoundTowArray(cArray,wArray);
    }

    deSubBytes(cArray);

    deShiftRows(cArray);

    addRoundKey(cArray,0);

    convertArrayToStr(cArray,c + k);

  }
}

完整的程式程式碼

Linux版本

aes.h

#ifndef AES_H
#define AES_H

/**
 * 引數 p: 明文的字串陣列。
 * 引數 plen: 明文的長度,長度必須為16的倍數。
 * 引數 key: 金鑰的字串陣列。
 */
void aes(char *p,char *key);

/**
 * 引數 c: 密文的字串陣列。
 * 引數 clen: 密文的長度,長度必須為16的倍數。
 * 引數 key: 金鑰的字串陣列。
 */
void deAes(char *c,char *key);

#endif

aes.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes.h"

/**
 * S盒
 */
static const int S[16][16] = { 0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16 };

/**
 * 逆S盒
 */
static const int S2[16][16] = { 0x52,0x16,0x63,0x7d };

/**
 * 獲取整形資料的低8位的左4個位
 */
static int getLeft4Bit(int num) {
	int left = num & 0x000000f0;
	return left >> 4;
}

/**
 * 獲取整形資料的低8位的右4個位
 */
static int getRight4Bit(int num) {
	return num & 0x0000000f;
}
/**
 * 根據索引,從S盒中獲得元素
 */
static int getNumFromSBox(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S[row][col];
}

/**
 * 把一個字元轉變成整型
 */
static int getIntFromChar(char c) {
	int result = (int) c;
	return result & 0x000000ff;
}

/**
 * 把16個字元轉變成4X4的陣列,
 * 該矩陣中位元組的排列順序為從上到下,
 * 從左到右依次排列。
 */
static void convertToIntArray(char *str,int pa[4][4]) {
	int k = 0;
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++) {
			pa[j][i] = getIntFromChar(str[k]);
			k++;
		}
}

/**
 * 列印4X4的陣列
 */
static void printArray(int a[4][4]) {
	for(int i = 0; i < 4; i++){
		for(int j = 0; j < 4; j++)
			printf("a[%d][%d] = 0x%x ",i,j,a[i][j]);
		printf("\n");
	}
	printf("\n");
}

/**
 * 列印字串的ASSCI,
 * 以十六進位制顯示。
 */
static void printASSCI(char *str,int len) {
	for(int i = 0; i < len; i++)
		printf("0x%x ",getIntFromChar(str[i]));
	printf("\n");
}

/**
 * 把連續的4個字符合併成一個4位元組的整型
 */
static int getWordFromStr(char *str) {
	int one = getIntFromChar(str[0]);
	one = one << 24;
	int two = getIntFromChar(str[1]);
	two = two << 16;
	int three = getIntFromChar(str[2]);
	three = three << 8;
	int four = getIntFromChar(str[3]);
	return one | two | three | four;
}

/**
 * 把一個4位元組的數的第一、二、三、四個位元組取出,
 * 入進一個4個元素的整型數組裡面。
 */
static void splitIntToArray(int num,int array[4]) {
	int one = num >> 24;
	array[0] = one & 0x000000ff;
	int two = num >> 16;
	array[1] = two & 0x000000ff;
	int three = num >> 8;
	array[2] = three & 0x000000ff;
	array[3] = num & 0x000000ff;
}

/**
 * 將陣列中的元素迴圈左移step位
 */
static void leftLoop4int(int array[4],int step) {
	int temp[4];
	for(int i = 0; i < 4; i++)
		temp[i] = array[i];

	int index = step % 4 == 0 ? 0 : step % 4;
	for(int i = 0; i < 4; i++){
		array[i] = temp[index];
		index++;
		index = index % 4;
	}
}

/**
 * 把陣列中的第一、二、三和四元素分別作為
 * 4位元組整型的第一、二、三和四位元組,合併成一個4位元組整型
 */
static int mergeArrayToInt(int array[4]) {
	int one = array[0] << 24;
	int two = array[1] << 16;
	int three = array[2] << 8;
	int four = array[3];
	return one | two | three | four;
}

/**
 * 常量輪值表
 */
static const int Rcon[10] = { 0x01000000,int round) {
	int numArray[4];
	splitIntToArray(num,numArray);
	leftLoop4int(numArray,1);//字迴圈

	//位元組代換
	for(int i = 0; i < 4; i++)
		numArray[i] = getNumFromSBox(numArray[i]);

	int result = mergeArrayToInt(numArray);
	return result ^ Rcon[round];
}

//金鑰對應的擴充套件陣列
static int w[44];

/**
 * 擴充套件金鑰,結果是把w[44]中的每個元素初始化
 */
static void extendKey(char *key) {
	for(int i = 0; i < 4; i++)
		w[i] = getWordFromStr(key + i * 4);

	for(int i = 4,j = 0; i < 44; i++) {
		if( i % 4 == 0) {
			w[i] = w[i - 4] ^ T(w[i - 1],j);
			j++;//下一輪
		}else {
			w[i] = w[i - 4] ^ w[i - 1];
		}
	}

}

/**
 * 輪金鑰加
 */
static void addRoundKey(int array[4][4],int round) {
	int warray[4];
	for(int i = 0; i < 4; i++) {

		splitIntToArray(w[ round * 4 + i],warray);

		for(int j = 0; j < 4; j++) {
			array[j][i] = array[j][i] ^ warray[j];
		}
	}
}

/**
 * 位元組代換
 */
static void subBytes(int array[4][4]){
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			array[i][j] = getNumFromSBox(array[i][j]);
}

/**
 * 行移位
 */
static void shiftRows(int array[4][4]) {
	int rowTwo[4],rowFour[4];
	//複製狀態矩陣的第2,4行
	for(int i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}
	//迴圈左移相應的位數
	leftLoop4int(rowTwo,1);
	leftLoop4int(rowThree,2);
	leftLoop4int(rowFour,3);

	//把左移後的行復制回狀態矩陣中
	for(int i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}

/**
 * 列混合要用到的矩陣
 */
static const int colM[4][4] = { 2,2 };

static int GFMul2(int s) {
	int result = s << 1;
	int a7 = result & 0x00000100;

	if(a7 != 0) {
		result = result & 0x000000ff;
		result = result ^ 0x1b;
	}

	return result;
}

static int GFMul3(int s) {
	return GFMul2(s) ^ s;
}

static int GFMul4(int s) {
	return GFMul2(GFMul2(s));
}

static int GFMul8(int s) {
	return GFMul2(GFMul4(s));
}

static int GFMul9(int s) {
	return GFMul8(s) ^ s;
}

static int GFMul11(int s) {
	return GFMul9(s) ^ GFMul2(s);
}

static int GFMul12(int s) {
	return GFMul8(s) ^ GFMul4(s);
}

static int GFMul13(int s) {
	return GFMul12(s) ^ s;
}

static int GFMul14(int s) {
	return GFMul12(s) ^ GFMul2(s);
}

/**
 * GF上的二元運算
 */
static int GFMul(int n,int s) {
	int result;

	if(n == 1)
		result = s;
	else if(n == 2)
		result = GFMul2(s);
	else if(n == 3)
		result = GFMul3(s);
	else if(n == 0x9)
		result = GFMul9(s);
	else if(n == 0xb)//11
		result = GFMul11(s);
	else if(n == 0xd)//13
		result = GFMul13(s);
	else if(n == 0xe)//14
		result = GFMul14(s);

	return result;
}
/**
 * 列混合
 */
static void mixColumns(int array[4][4]) {

	int tempArray[4][4];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++){
			array[i][j] = GFMul(colM[i][0],tempArray[1][j]) 
				^ GFMul(colM[i][2],tempArray[3][j]);
		}
}
/**
 * 把4X4陣列轉回字串
 */
static void convertArrayToStr(int array[4][4],char *str) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			*str++ = (char)array[j][i];	
}
/**
 * 檢查金鑰長度
 */
static int checkKeyLen(int len) {
	if(len == 16)
		return 1;
	else
		return 0;
}

/**
 * 引數 p: 明文的字串陣列。
 * 引數 plen: 明文的長度。
 * 引數 key: 金鑰的字串陣列。
 */
void aes(char *p,char *key){

	int keylen = strlen(key);
	if(plen == 0 || plen % 16 != 0) {
		printf("明文字元長度必須為16的倍數!\n");
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("金鑰字元長度錯誤!長度必須為16、24和32。當前長度為%d\n",keylen);
		exit(0);
	}

	extendKey(key);//擴充套件金鑰
	int pArray[4][4];

	for(int k = 0; k < plen; k += 16) {	
		convertToIntArray(p + k,pArray);

		addRoundKey(pArray,0);//一開始的輪金鑰加

		for(int i = 1; i < 10; i++){//前9輪

			subBytes(pArray);//位元組代換

			shiftRows(pArray);//行移位

			mixColumns(pArray);//列混合

			addRoundKey(pArray,i);

		}

		//第10輪
		subBytes(pArray);//位元組代換

		shiftRows(pArray);//行移位

		addRoundKey(pArray,10);

		convertArrayToStr(pArray,p + k);
	}
}
/**
 * 根據索引從逆S盒中獲取值
 */
static int getNumFromS1Box(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S2[row][col];
}
/**
 * 逆位元組變換
 */
static void deSubBytes(int array[4][4]) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			array[i][j] = getNumFromS1Box(array[i][j]);
}
/**
 * 把4個元素的陣列迴圈右移step位
 */
static void rightLoop4int(int array[4],int step) {
	int temp[4];
	for(int i = 0; i < 4; i++)
		temp[i] = array[i];

	int index = step % 4 == 0 ? 0 : step % 4;
	index = 3 - index;
	for(int i = 3; i >= 0; i--) {
		array[i] = temp[index];
		index--;
		index = index == -1 ? 3 : index;
	}
}

/**
 * 逆行移位
 */
static void deShiftRows(int array[4][4]) {
	int rowTwo[4],rowFour[4];
	for(int i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	rightLoop4int(rowTwo,1);
	rightLoop4int(rowThree,2);
	rightLoop4int(rowFour,3);

	for(int i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}
/**
 * 逆列混合用到的矩陣
 */
static const int deColM[4][4] = { 0xe,0xb,0xd,0x9,0xe,0xe };

/**
 * 逆列混合
 */
static void deMixColumns(int array[4][4]) {
	int tempArray[4][4];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++){
			array[i][j] = GFMul(deColM[i][0],tempArray[0][j]) ^ GFMul(deColM[i][1],tempArray[1][j]) 
				^ GFMul(deColM[i][2],tempArray[2][j]) ^ GFMul(deColM[i][3],tempArray[3][j]);
		}
}
/**
 * 把兩個4X4陣列進行異或
 */
static void addRoundTowArray(int aArray[4][4],int bArray[4][4]) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			aArray[i][j] = aArray[i][j] ^ bArray[i][j];
}
/**
 * 從4個32位的金鑰字中獲得4X4陣列,
 * 用於進行逆列混合
 */
static void getArrayFrom4W(int i,int array[4][4]) {
	int index = i * 4;
	int colOne[4],colTwo[4],colThree[4],colFour[4];
	splitIntToArray(w[index],colOne);
	splitIntToArray(w[index + 1],colTwo);
	splitIntToArray(w[index + 2],colThree);
	splitIntToArray(w[index + 3],colFour);

	for(int i = 0; i < 4; i++) {
		array[i][0] = colOne[i];
		array[i][1] = colTwo[i];
		array[i][2] = colThree[i];
		array[i][3] = colFour[i];
	}

}

/**
 * 引數 c: 密文的字串陣列。
 * 引數 clen: 密文的長度。
 * 引數 key: 金鑰的字串陣列。
 */
void deAes(char *c,char *key) {

	int keylen = strlen(key);
	if(clen == 0 || clen % 16 != 0) {
		printf("密文字元長度必須為16的倍數!現在的長度為%d\n",clen);
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("金鑰字元長度錯誤!長度必須為16、24和32。當前長度為%d\n",keylen);
		exit(0);
	}

	extendKey(key);//擴充套件金鑰
	int cArray[4][4];
	for(int k = 0; k < clen; k += 16) {
		convertToIntArray(c + k,cArray);


		addRoundKey(cArray,10);

		int wArray[4][4];
		for(int i = 9; i >= 1; i--) {
			deSubBytes(cArray);

			deShiftRows(cArray);

			deMixColumns(cArray);
			getArrayFrom4W(i,wArray);
			deMixColumns(wArray);

			addRoundTowArray(cArray,wArray);
		}

		deSubBytes(cArray);

		deShiftRows(cArray);

		addRoundKey(cArray,0);

		convertArrayToStr(cArray,c + k);

	}
}

main.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "aes.h"

#define MAXLEN 1024

void getString(char *str,int len){

	int slen = read(0,str,len);
	for(int i = 0; i < slen; i++,str++){
		if(*str == '\n'){
			*str = '\0';
			break;
		}
	}
}

void printASCCI(char *str,int len) {
	int c;
	for(int i = 0; i < len; i++) {
		c = (int)*str++;
		c = c & 0x000000ff;
		printf("0x%x ",c);
	}
	printf("\n");
}

/**
 * 從標準輸入中讀取使用者輸入的字串
 */
void readPlainText(char *str,int *len) {
	int plen;
	while(1) {
		getString(str,MAXLEN);
		plen = strlen(str);
		if(plen != 0 && plen % 16 == 0) {
			printf("你輸入的明文為:%s\n",str);
			break;
		}else{
			printf("明文字元長度必須為16的倍數,現在的長度為%d\n",plen);
		}
	}
	*len = plen;
}
/**
 * 把字串寫進檔案
 */
void writeStrToFile(char *str,int len,char *fileName) {
	FILE *fp;
	fp = fopen(fileName,"wb");
	for(int i = 0; i < len; i++)
		putc(str[i],fp);
	fclose(fp);
}


void aesStrToFile(char *key) {

	char p[MAXLEN];
	int plen;
	printf("請輸入你的明文,明文字元長度必須為16的倍數\n");
	readPlainText(p,&plen);
	printf("進行AES加密..................\n");

	aes(p,plen,key);//AES加密

	printf("加密完後的明文的ASCCI為:\n");
	printASCCI(p,plen);
	char fileName[64];
	printf("請輸入你想要寫進的檔名,比如'test.txt':\n");
	if(scanf("%s",fileName) == 1) {	
		writeStrToFile(p,fileName);
		printf("已經將密文寫進%s中了,可以在執行該程式的當前目錄中找到它。\n",fileName);
	}
}
/**
 * 從檔案中讀取字串
 */
int readStrFromFile(char *fileName,char *str) {
	FILE *fp = fopen(fileName,"rb");
	if(fp == NULL) {
		printf("開啟檔案出錯,請確認檔案存在當前目錄下!\n");
		exit(0);
	}

	int i;
	for(i = 0; i < MAXLEN && (str[i] = getc(fp)) != EOF; i++);

	if(i >= MAXLEN) {
		printf("解密檔案過大!\n");
		exit(0);
	}

	str[i] = '\0';
	fclose(fp);
	return i;
}


void deAesFile(char *key) {
	char fileName[64];
	char c[MAXLEN];//密文字串
	printf("請輸入要解密的檔名,該檔案必須和本程式在同一個目錄\n");
	if(scanf("%s",fileName) == 1) {
		int clen = readStrFromFile(fileName,c);
		printf("開始解密.........\n");
		deAes(c,clen,key);
		printf("解密後的明文ASCII為:\n");
		printASCCI(c,clen);
		printf("明文為:%s\n",c);
		writeStrToFile(c,fileName);
		printf("現在可以開啟%s來檢視解密後的密文了!\n",fileName);
	}
}

void aesFile(char *key) {
	char fileName[64];
	char fileP[MAXLEN];

	printf("請輸入要加密的檔名,該檔案必須和本程式在同一個目錄\n");
	if(scanf("%s",fileName) == 1) {
		readStrFromFile(fileName,fileP);
		int plen = strlen(fileP);
		printf("開始加密.........\n");
		printf("加密前檔案中字元的ASCII為:\n");
		printASCCI(fileP,plen);

		aes(fileP,key);//開始加密

		printf("加密後的密文ASCII為:\n");
		printASCCI(fileP,plen);
		writeStrToFile(fileP,fileName);
		printf("已經將加密後的密文寫進%s中了\n",fileName);
	}
}

int main(int argc,char const *argv[]) {

	char key[17];
	printf("請輸入16個字元的金鑰:\n");
	int klen;
	while(1){
		getString(key,17);
		klen = strlen(key);
		if(klen != 16){
			printf("請輸入16個字元的金鑰,當前金鑰的長度為%d\n",klen);
		}else{
			printf("你輸入的金鑰為:%s\n",key);
			break;
		}
	}

	printf("輸入's'表示要加密輸入的字串,並將加密後的內容寫入到檔案\n");
	printf("請輸入要功能選項並按回車,輸入'f'表示要加密檔案\n");
	printf("輸入'p'表示要解密檔案\n");
	char c;
	if(scanf("%s",&c) == 1) {
		if(c == 's')
			aesStrToFile(key);//用AES加密字串,並將字串寫進檔案中
		else if(c == 'p')
			deAesFile(key);//把檔案中的密文解密,並寫回檔案中
		else if(c == 'f')//用AES加密檔案
			aesFile(key);
	}
	return 0;
}

通過下面的gcc命令來編譯執行:

gcc -o aes aes.c main.c

VC6.0版本

由於VC6.0的編譯器比較坑,要先宣告,後使用變數,故要對程式碼進行相應的修改。

aes.h

#ifndef MY_AES_H
#define MY_AES_H

/**
 * 引數 p: 明文的字串陣列。
 * 引數 plen: 明文的長度,char *key);

#endif

aes.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "aes.h"

/**
 * S盒
 */
static const int S[16][16] = { 0x63,int pa[4][4]) {
	int k = 0;
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++) {
			pa[j][i] = getIntFromChar(str[k]);
			k++;
		}
}

/**
 * 列印4X4的陣列
 */
static void printArray(int a[4][4]) {
	int i,j;
	for(i = 0; i < 4; i++){
		for(j = 0; j < 4; j++)
			printf("a[%d][%d] = 0x%x ",int len) {
	int i;
	for(i = 0; i < len; i++)
		printf("0x%x ",getIntFromChar(str[i]));
	printf("\n");
}

/**
 * 把連續的4個字符合併成一個4位元組的整型
 */
static int getWordFromStr(char *str) {
	int one,two,three,four;
	one = getIntFromChar(str[0]);
	one = one << 24;
	two = getIntFromChar(str[1]);
	two = two << 16;
	three = getIntFromChar(str[2]);
	three = three << 8;
	four = getIntFromChar(str[3]);
	return one | two | three | four;
}

/**
 * 把一個4位元組的數的第一、二、三、四個位元組取出,
 * 入進一個4個元素的整型數組裡面。
 */
static void splitIntToArray(int num,int array[4]) {
	int one,three;
	one = num >> 24;
	array[0] = one & 0x000000ff;
	two = num >> 16;
	array[1] = two & 0x000000ff;
	three = num >> 8;
	array[2] = three & 0x000000ff;
	array[3] = num & 0x000000ff;
}

/**
 * 將陣列中的元素迴圈左移step位
 */
static void leftLoop4int(int array[4],int step) {
	int temp[4];
	int i;
	int index;
	for(i = 0; i < 4; i++)
		temp[i] = array[i];

	index = step % 4 == 0 ? 0 : step % 4;
	for(i = 0; i < 4; i++){
		array[i] = temp[index];
		index++;
		index = index % 4;
	}
}

/**
 * 把陣列中的第一、二、三和四元素分別作為
 * 4位元組整型的第一、二、三和四位元組,合併成一個4位元組整型
 */
static int mergeArrayToInt(int array[4]) {
	int one = array[0] << 24;
	int two = array[1] << 16;
	int three = array[2] << 8;
	int four = array[3];
	return one | two | three | four;
}

/**
 * 常量輪值表
 */
static const int Rcon[10] = { 0x01000000,int round) {
	int numArray[4];
	int i;
	int result;
	splitIntToArray(num,1);//字迴圈

	//位元組代換
	for(i = 0; i < 4; i++)
		numArray[i] = getNumFromSBox(numArray[i]);

	result = mergeArrayToInt(numArray);
	return result ^ Rcon[round];
}

//金鑰對應的擴充套件陣列
static int w[44];
/**
 * 列印W陣列
 */
static void printW() {
	int i,j;
	for(i = 0,j = 1; i < 44; i++,j++){
		printf("w[%d] = 0x%x ",w[i]);
		if(j % 4 == 0)
			printf("\n");
	}
	printf("\n");
}


/**
 * 擴充套件金鑰,結果是把w[44]中的每個元素初始化
 */
static void extendKey(char *key) {
	int i,j;
	for(i = 0; i < 4; i++)
		w[i] = getWordFromStr(key + i * 4);

	for(i = 4,int round) {
	int warray[4];
	int i,j;
	for(i = 0; i < 4; i++) {

		splitIntToArray(w[ round * 4 + i],warray);

		for(j = 0; j < 4; j++) {
			array[j][i] = array[j][i] ^ warray[j];
		}
	}
}

/**
 * 位元組代換
 */
static void subBytes(int array[4][4]){
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			array[i][j] = getNumFromSBox(array[i][j]);
}

/**
 * 行移位
 */
static void shiftRows(int array[4][4]) {
	int rowTwo[4],rowFour[4];
	int i;
	for(i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	leftLoop4int(rowTwo,3);

	for(i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}

/**
 * 列混合要用到的矩陣
 */
static const int colM[4][4] = { 2,int s) {
	int result;

	if(n == 1)
		result = s;
	else if(n == 2)
		result = GFMul2(s);
	else if(n == 3)
		result = GFMul3(s);
	else if(n == 0x9)
		result = GFMul9(s);
	else if(n == 0xb)//11
		result = GFMul11(s);
	else if(n == 0xd)//13
		result = GFMul13(s);
	else if(n == 0xe)//14
		result = GFMul14(s);

	return result;
}
/**
 * 列混合
 */
static void mixColumns(int array[4][4]) {

	int tempArray[4][4];
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++){
			array[i][j] = GFMul(colM[i][0],tempArray[1][j])
				^ GFMul(colM[i][2],char *str) {
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			*str++ = (char)array[j][i];
}
/**
 * 檢查金鑰長度
 */
static int checkKeyLen(int len) {
	if(len == 16)
		return 1;
	else
		return 0;
}


/**
 * 引數 p: 明文的字串陣列。
 * 引數 plen: 明文的長度。
 * 引數 key: 金鑰的字串陣列。
 */
void aes(char *p,char *key){

	int keylen = strlen(key);
	int pArray[4][4];
	int k,i;

	if(plen == 0 || plen % 16 != 0) {
		printf("明文字元長度必須為16的倍數!\n");
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("金鑰字元長度錯誤!長度必須為16。當前長度為%d\n",keylen);
		exit(0);
	}

	extendKey(key);//擴充套件金鑰

	for(k = 0; k < plen; k += 16) {
		convertToIntArray(p + k,0);//一開始的輪金鑰加

		for(i = 1; i < 10; i++){

			subBytes(pArray);//位元組代換

			shiftRows(pArray);//行移位

			mixColumns(pArray);//列混合

			addRoundKey(pArray,i);

		}

		subBytes(pArray);//位元組代換

		shiftRows(pArray);//行移位

		addRoundKey(pArray,p + k);
	}
}
/**
 * 根據索引從逆S盒中獲取值
 */
static int getNumFromS1Box(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S2[row][col];
}
/**
 * 逆位元組變換
 */
static void deSubBytes(int array[4][4]) {
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			array[i][j] = getNumFromS1Box(array[i][j]);
}
/**
 * 把4個元素的陣列迴圈右移step位
 */
static void rightLoop4int(int array[4],int step) {
	int temp[4];
	int i;
	int index;
	for(i = 0; i < 4; i++)
		temp[i] = array[i];

	index = step % 4 == 0 ? 0 : step % 4;
	index = 3 - index;
	for(i = 3; i >= 0; i--) {
		array[i] = temp[index];
		index--;
		index = index == -1 ? 3 : index;
	}
}

/**
 * 逆行移位
 */
static void deShiftRows(int array[4][4]) {
	int rowTwo[4],rowFour[4];
	int i;
	for(i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	rightLoop4int(rowTwo,3);

	for(i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}
/**
 * 逆列混合用到的矩陣
 */
static const int deColM[4][4] = { 0xe,0xe };

/**
 * 逆列混合
 */
static void deMixColumns(int array[4][4]) {
	int tempArray[4][4];
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++){
			array[i][j] = GFMul(deColM[i][0],tempArray[1][j])
				^ GFMul(deColM[i][2],int bArray[4][4]) {
	int i,j;
	for(i = 0; i < 4; i++)
		for(j = 0; j < 4; j++)
			aArray[i][j] = aArray[i][j] ^ bArray[i][j];
}
/**
 * 從4個32位的金鑰字中獲得4X4陣列,
 * 用於進行逆列混合
 */
static void getArrayFrom4W(int i,int array[4][4]) {
	int index,j;
	int colOne[4],colFour[4];
	index = i * 4;
	splitIntToArray(w[index],colFour);

	for(j = 0; j < 4; j++) {
		array[j][0] = colOne[j];
		array[j][1] = colTwo[j];
		array[j][2] = colThree[j];
		array[j][3] = colFour[j];
	}

}

/**
 * 引數 c: 密文的字串陣列。
 * 引數 clen: 密文的長度。
 * 引數 key: 金鑰的字串陣列。
 */
void deAes(char *c,char *key) {

	int cArray[4][4];
	int keylen,k;
	keylen = strlen(key);
	if(clen == 0 || clen % 16 != 0) {
		printf("密文字元長度必須為16的倍數!現在的長度為%d\n",keylen);
		exit(0);
	}

	extendKey(key);//擴充套件金鑰

	for(k = 0; k < clen; k += 16) {
		int i;
		int wArray[4][4];

		convertToIntArray(c + k,cArray);

		
		
		

		addRoundKey(cArray,10);

		for(i = 9; i >= 1; i--) {
			deSubBytes(cArray);

			deShiftRows(cArray);

			deMixColumns(cArray);
			getArrayFrom4W(i,c + k);

	}
}

有不少初學者可能在使用AES實現的VC版本時,會出現沒main函式的問題。其實直接匯入VC編譯是不行的,這裡給出的只是標頭檔案 aes.h 和實現的 aes.cpp 檔案,需要通過include來包含使用,假設main函式所在的檔案 main.cpp,並且與 aes.h 、 aes.cpp 檔案在同一目錄下,則需要像下面這樣使用:

 #include "aes.h"
 // 其它標頭檔案
 
 int main(int argc,char const *argv[]) {
  // 加密,其中plain是明文字元陣列, len是長度, key是金鑰
  aes(plain,len,key);
  //解密,其中ciphertext是密文字元陣列, len是長度, key是金鑰
  deAes(ciphertext,key);
  }

很高興這篇文章能給大家帶來幫助,我現在主要做資訊保安方面的工作。下面是我建立的公眾號,會不定期分享一些資訊保安方面的技術文章,歡迎關注~