AES演算法的實現與分析
隨著現代密碼分析水平、晶片處理能力和計算技術的不斷進步,高階加密標準AES的Rijndael演算法將在各行業各部門獲得廣泛的應用,成為虛擬專用網、SONET、遠端訪問伺服器、高速ATM/以太路由器、行動通訊、衛星通訊、電子金融業務等的加密演算法,並逐漸取代DES在IPSec、SSL和ATM中的加密功能。目前,IEEE 802.11i草案已經定義了AES加密的兩種不同執行模式,成功解決了無限區域網標準中的諸多安全問題。在這種情形下,AES演算法的安全性及其快速實現問題顯得格外突出,本文對此進行了全面的論述,希望能為有意進行這一方面研究和應用的同行提供有益的參考。
文章闡述了Rijndael演算法的設計特色,介紹了AES在密碼分析方面國內外已有的一些理論分析成果,描述了AES演算法採用軟體和硬體實現方案。此外,本文章從數學基礎的知識上闡明瞭AES演算法的四個步驟。從AES演算法抵抗強力攻擊能力,抵抗差分分析和線性密碼分析的能力,抵抗滲透攻擊能力,抵抗代數計算攻擊能力,抵抗XSL攻擊能力,弱金鑰的分析這幾個方面進行了分析從而說明AES的安全效能。我們根據演算法的安全性、代價以及演算法與實現特性的原則實現了AES的演算法,從金鑰編排方案分析了金鑰的設計準則和選取。
隨著對稱密碼的發展,3DES用軟體實現速度相對較慢,它使用的64位分組長度顯得不夠高效和安全的缺點使得需要一種新的高階加密標準來替代它。AES的全稱是 Advanced Encryption Standard,即高階加密標準。該專案由美國國家標準技術研究所 (NIST)於1997年開始啟動並徵集演算法,在2000年確定採用Rijndael作為其最終演算法,並於 2001年被美國商務部部長批准為新的聯邦資訊加密標準 (FIPS PUB 197),該標準的正式生效日期是2002年5月26日。2000年10月2日,NIST對Rijndael做 出了最終評估。
AES是一個迭代的、對稱金鑰分組的密碼,它可以使用128、192和256位金鑰,並且用128位(16位元組)分組加密和解密資料。與公共金鑰密碼使用金鑰對不同,對稱金鑰密碼使用相同的金鑰加密和解密資料。通過分組密碼返回的加密資料的位數與輸入資料相同。迭代加密使用一個迴圈結構,在該迴圈中重複置換(permutations)和替換(substitutions)輸入資料。
一個密碼演算法的有效性首先體現在可靠的安全性上。Rijndael演算法設計採用針對差分和線性密碼分析提出的寬軌跡策略(WTS),其最大優勢是可以給出演算法最佳差分特徵的概率以及最佳線性逼近偏差的界;使用簡單的部件組織成清晰的結構,便於演算法安全性的分析。當然,在密碼學界永遠沒有絕對的安全,Rijndael演算法也不例外,如其明顯的代數結構對安全性的潛在威脅已經受到一些學者的質疑。本文從簡化演算法攻擊、演算法結構性質分析以及密碼分析的進展等方面對AES演算法的密碼分析狀況進行討論。
目前尚未出現對完整Rijndael演算法的成功攻擊,只提出了幾種針對簡化演算法的攻擊方法。最有名的當數密碼設計者自己提出的Square攻擊,其主要思想是利用第4輪位元組替換前後平衡性的改變來猜測金鑰位元組,對128位元金鑰下4到6輪簡化演算法有效。Biham[2]等對Square攻擊進行改進,時間複雜度降為原來的一半,並認為顛倒輪金鑰的順序可將攻擊複雜度降低28。Lucks[3]利用金鑰生成演算法的弱點,將Square攻擊的金鑰長度擴充套件到192和256位元,攻擊7輪簡化演算法比窮盡搜尋快。Ferguson[4]利用“部分和”技術將6輪Square攻擊的複雜度從272降到244,並推廣到7輪和8輪簡化演算法,指出金鑰生成演算法中幾個違背設計準則的特性,利用慢擴散性設計了一個針對256位元金鑰下9輪簡化演算法的金鑰相關攻擊方案。
Gilbert[5]利用區域性函式間的衝突特性對4到7輪簡化演算法進行了攻擊。Cheon等將5輪不可能差分攻擊推廣到6輪,複雜度高於相應的Square攻擊,但仍快於密鑰窮盡搜尋。Koeune[6]描述了一種計時攻擊,通過對每個金鑰數千次的測量,展現攻破一個不良的現實設計的過程。Golic[7]則指出AES演算法雖然能夠通過乘法掩蓋來抵抗簡單能量攻擊(SPA),對差分能量攻擊(DPA)卻無能為力。
- 首先宣告一個Aes的類
功能描述:以面向物件的方式,建立了一個類物件,包含了AES加密過程中要實現各項功能的函式,這樣能夠清晰的看到整個加密程式的結構,有利於對程式的模組化管理。
實現程式碼:
class Aes
{
public:
~Aes();
Aes();
Aes(); //建構函式用金鑰長度keySize和金鑰內容keyBytes作為引數。
void Cipher(); // 加密函式,input 為16位為要加密的明文
InvCipher(); // 解密函式,input 為16位要解密的密文
private:
int Nb; int Nk; int Nr;
unsigned char key[32];
unsigned char w[16*15];
SetNbNkNr(int keySize); //設定輪數
AddRoundKey(int round); //輪金鑰加
SubBytes(); //S盒位元組代換
InvSubBytes(); //逆S盒位元組代換
ShiftRows(); //行移位
InvShiftRows();
MixColumns(); //列混淆
InvMixColumns();
KeyExpansion(); //金鑰擴充套件
unsigned char* SubWord(unsigned char* word); //金鑰S盒字代換
unsigned char* RotWord(unsigned char* word); //金鑰移位
}
- 成員函式的實現:
- 迭代分組列數、金鑰列數、迭代次數設定函式
功能描述:因為AES分組長度為128 bits,Nb固定為4。再根據介面金鑰長度視窗keySize的選擇,判斷是金鑰是128、192、256位元,然後設定金鑰列數Nk,然後可得到Nr。(根據第2章“輪數的確定”一節)
實現程式碼:
Aes::SetNbNkNr(int keySize)
{
Nb=4;
if(keySize==Bits128)
{
Nk=4; //4*4位元組,128位金鑰,10輪加密
Nr=10;
}
else if(keySize==Bits192)
{
Nk=6; //6*4位元組,192位金鑰,12輪加密
Nr=12;
}
else if(keySize==Bits256)
{
Nk=8; //8*4位元組,256位金鑰,14輪加密
Nr=14;
}
}
- 建構函式
功能描述:實現了設定金鑰塊數,輪數,並且將設定了密碼,和為金鑰擴充套件提前做的初始化。
實現程式碼:
Aes::Aes(int keysize,unsigned char* keyBytes)
{
SetNbNkNr(keysize); //設定金鑰塊數,輪數
memcpy(key,keyBytes,keysize); //字串拷貝函式,把keyBytes的keysize個字元複製到key中
KeyExpansion(); //金鑰擴充套件,必須提前做的初始化
}
- 金鑰擴充套件函式
功能描述:加密和解密過程分別需要Nr+1個子金鑰。所以需要對金鑰進行擴充套件。
實現方式:主金鑰的擴充套件首先將從介面輸入的子金鑰拷貝,並按一維陣列排成4*Nk矩陣,然後取Nk列,進行RotWord()移位變換,將第一位移到第四位,再進行SubWord() 位元組代換,最後再和上一個金鑰的第1列做模二加法,得到新的子金鑰塊的第1列金鑰,然後第2、3、4列是直接由前一列和上一子金鑰模二加法得到。
實現程式碼 :
Aes::KeyExpansion()
{
memset(w,0,16*15);
for(int row=0;row<Nk;row++) //拷貝seed 金鑰
{
w[4*row+0] = key[4*row];
w[4*row+1] = key[4*row+1];
w[4*row+2] = key[4*row+2];
w[4*row+3] = key[4*row+3];
}
byte* temp = new byte[4];
for(row=Nk;row<4*(Nr+1);row++)
{
temp[0]=w[4*row-4]; //當前列的前一列
temp[1]=w[4*row-3];
temp[2]=w[4*row-2];
temp[3]=w[4*row-1];
if(row%Nk==0) //逢nk時,對當前列的前一列作特殊處理
{
temp=SubWord(RotWord(temp));//先移位,再代換,最後和輪常量異或
temp[0] = (byte)( (int)temp[0] ^ (int) AesRcon[4*(row/Nk)+0] );
temp[1] = (byte)( (int)temp[1] ^ (int) AesRcon[4*(row/Nk)+1] );
temp[2] = (byte)( (int)temp[2] ^ (int) AesRcon[4*(row/Nk)+2] );
temp[3] = (byte)( (int)temp[3] ^ (int) AesRcon[4*(row/Nk)+3] );
}
else if ( Nk > 6 && (row % Nk == 4) ) //這個還沒有搞清楚
{
temp = SubWord(temp);
}
// w[row] = w[row-Nk] xor temp
w[4*row+0] = (byte) ( (int) w[4*(row-Nk)+0] ^ (int)temp[0] );
w[4*row+1] = (byte) ( (int) w[4*(row-Nk)+1] ^ (int)temp[1] );
w[4*row+2] = (byte) ( (int) w[4*(row-Nk)+2] ^ (int)temp[2] );
w[4*row+3] = (byte) ( (int) w[4*(row-Nk)+3] ^ (int)temp[3] );
} // for loop
}
- 輪金鑰加
功能描述:在每一輪的加密過程實現了將分組的待加密字串的第一列與一個金鑰字進行按位異或。
實現程式碼:
Aes::AddRoundKey(int round)
{
int i,j; //i行 j列 //因為金鑰w是一列一列排列的,即 k0 k4 k8 k12
for(j=0;j<4;j++) // k1 k5 k9 k13
{ // k2 k6 k10k14
for(i=0;i<4;i++) // k3 k7 k11k15
{ // 所以i行j列的下標是4*((round*4)+j)+i即16*round+4*j+i
State[i][j]=(unsigned char)((int)State[i][j]^(int)w[4*((round*4)+j)+i]);
}
}
}
- 位元組代換函式
功能描述:位元組代換是基於S盒(見附錄)的非線性轉換,它用於將輸入的每一個位元組通過一個簡單的查表操作,將其對映為別一個位元組。對映方法是:把輸入的位元組高4位作為S盒的行值,低4位作為列值,然後取出對應行和列的元素作為輸出。
程式碼實現:
Aes::SubBytes() {
int i,j;
for(j=0;j<4;j++)
{
for(i=0;i<4;i++)
{
State[i][j]=AesSbox[State[i][j]];
//因為 16*(State[i][j]>>4)+State[i][j]&0x0f=State[i][j]
}
}
}
- 行移位函式
功能描述:實現了行的迴圈移位操作,變換方法為:第0行不動,第1行迴圈左移一個位元組,第2行迴圈左移2個位元組,第3行迴圈左移3個位元組。
程式碼實現:
Aes::ShiftRows()
{
unsigned char temp[4*4];
int i,j;
for(j=0;j<4;j++)
{
for(i=0;i<4;i++)
{
temp[4*i+j]=State[i][j];
}
}
for(i=1;i<4;i++)
{
for(j=0;j<4;j++)
{
if(i==1)State[i][j]=temp[4*i+(j+1)%4]; //第二行左移1位
else if(i==2)State[i][j]=temp[4*i+(j+2)%4]; //第三行左移2位
else if(i==3)State[i][j]=temp[4*i+(j+3)%4]; //第四行左移3位
}
}
}
- 列混合變換函式
功能描述:列混合變換是一個替代操作,將原矩陣中的每個元素都是一行和一列對應元素的乘積之和。在MixColumns變換中,乘法和加法都是定義在GF()上的。(詳細原理請參考經“第二章6.4節”)
實現偽碼:
Aes::MixColumns(byte state[4,Nc])
{
begin
byte t[4]
for c=0 step Nc -1
for r=0 step 1 to 3
t[4]=state[r,c]
end for
for r=o step 1 to 3
state[r,c]=Ffmul(0x02,t[r])xor //Ffmul(x,y)返回兩個元素x和y的積
Ffmul(0x03,t[(r+1)mod 4]) xor
t[(r+2)mod 4] xor t[(r+3) mod 4]
end for
end for
end
}
- 加密函式
功能描述:
- 對給定的一個明文X,用State初始化為標準格式,並進行AddRoundKey()操作,將輪金鑰與State異或。
- 對前Nr-1輪中的每一輪,用S盒進行一次SubBytes代換操作;對State做一次ShiftRows行移位操作gmf對State做一次MixColumns列混淆操作;然後進行AddRoundKey操作。
- 依次進行SubBytes,ShiftRows,AddRoundKey操作。
- 將最後的State中的內容定義為密文。
程式碼實現:
void Aes::Cipher(unsigned char* input, unsigned char* output)
{
memset(&State[0][0],0,16);
for(int i=0;i<4*Nb;i++) //這裡是先寫列後寫行的,即輸入是一列一列的進來的
{
State[i%4][i/4]=input[i]; //換成先寫行後寫列也是可以的,只要在輸出時也是這樣就可以了
}
AddRoundKey(0); //輪金鑰加
for (int round = 1; round <= (Nr - 1); round++) // main round loop
{
SubBytes(); //位元組代換
ShiftRows(); //行移位
MixColumns(); //列混淆
AddRoundKey(round); //輪金鑰加
} // main round loop
SubBytes(); //位元組代換
ShiftRows(); //行移位
AddRoundKey(Nr); //輪金鑰加
// output = state
for (i = 0; i < (4 * Nb); i++)
{
output[i] = State[i % 4][ i / 4];
}
}
- MFC中主函式的實現
功能描述:實現了將可視介面中的輸入的資訊關聯變數、函式,並呼叫AES類中的各成員函式進行加密運算,最後再將結果在介面上顯示出來。
程式碼實現:
void CAesCodeDlg::OnBAesEn()
{
CString Text;
int keysize;
((CComboBox*)GetDlgItem(IDC_COMBO1))->GetWindowText(Text);
//將介面資訊關聯變數的函式
if (Text=="128bits") keysize=16; //選擇金鑰長度
else if (Text=="192bits") keysize=24;
else if (Text=="256bits") keysize=32;
else MessageBox("請選擇金鑰長度");
unsigned char inBuff[33],ouBuff[33];
memset(inBuff,0,33);
memset(ouBuff,0,33);
unsigned char keykey[35] ;
GetDlgItemText(IDC_key,( char*)keykey,keysize);
Aes aes(keysize,(unsigned char *)keykey); //宣告Aes類物件
GetDlgItemText(IDC_Input,(char*)inBuff,keysize);
if(strlen((char*)inBuff)>16)MessageBox("本例只能加密16位元組的字串,大於截斷");
aes.Cipher(inBuff,ouBuff); // 呼叫Aes成員函式Cipher進行加密
CString str="",strTmp; //實際輸出是32個字母或數字,否則ASCII碼值超出127的會變成亂碼。
for(int i=0;i<16;i++)
{
strTmp.Format("%02x",ouBuff[i]); //其實相當於把ouBuff的ASCII值這個數字以16進位制的形式輸出
str+=strTmp;
}
//MessageBox(str,"加密後");
SetDlgItemText(IDC_EAesEn,str);
}
加密使用的S盒
static unsigned char AesSbox[16*16]=
{// populate the Sbox matrix
/* 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
};
金鑰擴充套件的實現程式碼
金鑰移位函式
unsigned char* Aes::RotWord(unsigned char* word)
{
byte* temp = new byte[4];
temp[0] = word[1];
temp[1] = word[2];
temp[2] = word[3];
temp[3] = word[0];
return temp;
}
金鑰字代換函式
unsigned char* Aes::SubWord(unsigned char* word)
{
byte* temp = new byte[4];
for(int j=0;j<4;j++)
{
temp[j] = AesSbox[16*(word[j] >> 4)+(word[j] & 0x0f)]; //實際上也可以寫成AesSbox[[j]];因為兩者相等
}
return temp;
}
列混淆的實現程式碼
Aes::MixColumns()
{
unsigned char temp[4*4];
int i,j;
for(j=0;j<4;j++) //2 3 1 1 列混淆矩陣 Page107
{ //1 2 3 1
for(i=0;i<4;i++) //1 1 2 3
{ //3 1 1 2
temp[4*i+j]=State[i][j];
}
}
for(j=0;j<4;j++)
{
State[0][j] = (unsigned char) ( (int)gfmultby02(temp[0+j]) ^ (int)gfmultby03(temp[4*1+j]) ^
(int)gfmultby01(temp[4*2+j]) ^ (int)gfmultby01(temp[4*3+j]) );
State[1][j] = (unsigned char) ( (int)gfmultby01(temp[0+j]) ^ (int)gfmultby02(temp[4*1+j]) ^
(int)gfmultby03(temp[4*2+j]) ^ (int)gfmultby01(temp[4*3+j]) );
State[2][j] = (unsigned char) ( (int)gfmultby01(temp[0+j]) ^ (int)gfmultby01(temp[4*1+j]) ^
(int)gfmultby02(temp[4*2+j]) ^ (int)gfmultby03(temp[4*3+j]) );
State[3][j] = (unsigned char) ( (int)gfmultby03(temp[0+j]) ^ (int)gfmultby01(temp[4*1+j]) ^
(int)gfmultby01(temp[4*2+j]) ^ (int)gfmultby02(temp[4*3+j]) );
}
}
unsigned char Aes::gfmultby01(unsigned char b)
{
return b;
}
unsigned char Aes::gfmultby02(unsigned char b)
{
if (b < 0x80)
return (unsigned char)(int)(b <<1);
else
return (unsigned char)( (int)(b << 1) ^ (int)(0x1b) );
}
unsigned char Aes::gfmultby03(unsigned char b)
{
return (unsigned char) ( (int)gfmultby02(b) ^ (int)b );
}
unsigned char Aes::gfmultby09(unsigned char b)
{
return (unsigned char)( (int)gfmultby02(gfmultby02(gfmultby02(b))) ^ (int)b );
}
unsigned char Aes::gfmultby0b(unsigned char b)
{
return (unsigned char)( (int)gfmultby02(gfmultby02(gfmultby02(b))) ^
(int)gfmultby02(b) ^ (int)b );
}
unsigned char Aes::gfmultby0d(unsigned char b)
{
return (unsigned char)( (int)gfmultby02(gfmultby02(gfmultby02(b))) ^
(int)gfmultby02(gfmultby02(b)) ^ (int)(b) );
}
unsigned char Aes::gfmultby0e(unsigned char b)
{
return (unsigned char)( (int)gfmultby02(gfmultby02(gfmultby02(b))) ^
(int)gfmultby02(gfmultby02(b)) ^(int)gfmultby02(b) );
}