現代密碼學實驗3 AES加密算法

讚賞碼 & 聯繫方式 & 個人閒話

【實驗名稱】AES加密算法的實現

 

【實驗目的】

1、詳細理解AES算法的相關結構及基礎理論;

2、設計AES加密和解密軟件系統。

 

【實驗原理】

AES加密過程是在一個4×4的字節矩陣上運作,這個矩陣又稱爲“狀態(state)”,其初值就是一個明文區塊(矩陣中一個元素大小就是明文區塊中的一個Byte)。(Rijndael加密法因支持更大的區塊,其矩陣行數可視情況增加)加密時,各輪AES加密循環(除最後一輪外)均包含4個步驟:

1、AddRoundKey — 矩陣中的每一個字節都與該次輪祕鑰(round key)做XOR運算;每個子密鑰由密鑰生成方案產生。

2、SubBytes — 通過非線性的替換函數,用查找表的方式把每個字節替換成對應的字節。

3、ShiftRows — 將矩陣中的每個橫列進行循環式移位。

4、MixColumns — 爲了充分混合矩陣中各個直行的操作。這個步驟使用線性轉換來混合每列的四個字節。

 

【實驗內容】

實驗內容: 編程實現AES加密/解密算法。

代碼:(代碼解釋見代碼中註釋)

#include<stdio.h>
//S盒
static int S[16][16] = 
{/*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,
/*10*/0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
/*11*/0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
/*12*/0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
/*13*/0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
/*14*/0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
/*15*/0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };

//逆S盒
static int S2[16][16];


void S_ni()
{
	int i, j;
	for (i = 0; i < 16; i++)
	{
		for (j = 0; j < 16; j++)
		{
			int temp1, temp2;
			temp1 = S[i][j] & 0xf0;
			temp1 = temp1 >> 4;
			temp2 = S[i][j] & 0x0f;
			S2[temp1][temp2] = (i << 4) + j;
		}
	}
	/*
	for (i = 0; i < 16; i++)
	{
		for (j = 0; j < 16; j++)
		{
			printf("%x ", S2[i][j]);
		}
		printf("\n");
	}
	*/
}

//128bits的明文和密鑰
char key[17];
char input[17];


int SubBytes(int num) 
{//單個字符替換
	int row = (num & 0xf0) >> 4;
	int col = num & 0x0f;
	return S[row][col];
}


int SubBytes_ni(int num)
{//單個字符逆替換
	int row = (num & 0xf0) >> 4;
	int col = num & 0x0f;
	return S2[row][col];
}


void translate(int *in)
{//16字符替換
	int i;
	for (i = 0; i <= 3; i++)
	{
		int t[4];
		t[0] = (in[i] & 0xff000000) >> 24;
		t[1] = (in[i] & 0xff0000) >> 16;
		t[2] = (in[i] & 0xff00) >> 8;
		t[3] = in[i] & 0xff;
		t[0] = SubBytes(t[0]) << 24;
		t[1] = SubBytes(t[1]) << 16;
		t[2] = SubBytes(t[2]) << 8;
		t[3] = SubBytes(t[3]);
		in[i] = t[0] + t[1] + t[2] + t[3];
	}
	return;
}


void translate_ni(int *in)
{//16字符逆替換
	int i;
	for (i = 0; i <= 3; i++)
	{
		int t[4];
		t[0] = (in[i] & 0xff000000) >> 24;
		t[1] = (in[i] & 0xff0000) >> 16;
		t[2] = (in[i] & 0xff00) >> 8;
		t[3] = in[i] & 0xff;
		t[0] = SubBytes_ni(t[0]) << 24;
		t[1] = SubBytes_ni(t[1]) << 16;
		t[2] = SubBytes_ni(t[2]) << 8;
		t[3] = SubBytes_ni(t[3]);
		in[i] = t[0] + t[1] + t[2] + t[3];
	}
	return;
}


int x_mul(int a, int b)
{//x乘法
	int mi;
	int i;
	int result[8];
	int R = 0;
	int number = 128;
	for (mi = 7; mi >= 0; mi--)
	{
		if ((b & number) == number)
		{
			result[mi] = a;
			for (i = mi; i >= 1; i--)
			{
				if ((result[mi] & 128) == 128)
				{
					result[mi] = result[mi] << 1;
					result[mi] = result[mi] ^ 27;
				}
				else
				{
					result[mi] = result[mi] << 1;
				}
			}
		}
		else
		{
			result[mi] = 0;
		}
		number = number / 2;
	}
	//異或
	for (i = 0; i <= 7; i++)
	{
		R = R ^ result[i];
	}
	//int爲4字節,此處置前三字節爲0,避免之前左移的影響
	R = R & 0xff;
	return R;
}


void lineshift(int *in)
{//行移位
	int temp[16];
	int i;
	for (i = 0; i <= 3; i++)
	{
		temp[4 * i + 0] = (in[i] & 0xff000000);
		temp[4 * i + 1] = (in[i] & 0xff0000);
		temp[4 * i + 2] = (in[i] & 0xff00);
		temp[4 * i + 3] = in[i] & 0xff;
	}
	in[0] = temp[0] + temp[5] + temp[10] + temp[15];
	in[1] = temp[4] + temp[9] + temp[14] + temp[3];
	in[2] = temp[8] + temp[13] + temp[2] + temp[7];
	in[3] = temp[12] + temp[1] + temp[6] + temp[11];
}


void lineshift_ni(int *in)
{//逆行移位
	int temp[16];
	int i;
	for (i = 0; i <= 3; i++)
	{
		temp[4 * i + 0] = (in[i] & 0xff000000);
		temp[4 * i + 1] = (in[i] & 0xff0000);
		temp[4 * i + 2] = (in[i] & 0xff00);
		temp[4 * i + 3] = in[i] & 0xff;
	}
	in[0] = temp[0] + temp[13] + temp[10] + temp[7];
	in[1] = temp[4] + temp[1] + temp[14] + temp[11];
	in[2] = temp[8] + temp[5] + temp[2] + temp[15];
	in[3] = temp[12] + temp[9] + temp[6] + temp[3];
}


void mixcolumn(int *in)
{//列混合
	int temp[4][4];
	int s[4][4];
	int i;
	for (i = 0; i <= 3; i++)
	{
		temp[0][i] = (in[i] & 0xff000000) >> 24;
		temp[1][i] = (in[i] & 0xff0000) >> 16;
		temp[2][i] = (in[i] & 0xff00) >> 8;
		temp[3][i] = in[i] & 0xff;
	}
	
	for (i = 0; i <= 3; i++)
	{
		s[0][i] = (x_mul(temp[0][i], 2)) ^ (x_mul(temp[1][i], 3)) ^ temp[2][i] ^ temp[3][i];
		s[1][i] = temp[0][i] ^ (x_mul(temp[1][i], 2)) ^ (x_mul(temp[2][i], 3)) ^ temp[3][i];
		s[2][i] = temp[0][i] ^ temp[1][i] ^ (x_mul(temp[2][i], 2)) ^ (x_mul(temp[3][i], 3));
		s[3][i] = (x_mul(temp[0][i], 3)) ^ temp[1][i] ^ temp[2][i] ^ (x_mul(temp[3][i], 2));
	}

	in[0] = (s[0][0] << 24) + (s[1][0] << 16) + (s[2][0] << 8) + s[3][0];
	in[1] = (s[0][1] << 24) + (s[1][1] << 16) + (s[2][1] << 8) + s[3][1];
	in[2] = (s[0][2] << 24) + (s[1][2] << 16) + (s[2][2] << 8) + s[3][2];
	in[3] = (s[0][3] << 24) + (s[1][3] << 16) + (s[2][3] << 8) + s[3][3];

	return;

}


void mixcolumn_ni(int *in)
{//逆列混合
	int temp[4][4];
	int s[4][4];
	int i;
	for (i = 0; i <= 3; i++)
	{
		temp[0][i] = (in[i] & 0xff000000) >> 24;
		temp[1][i] = (in[i] & 0xff0000) >> 16;
		temp[2][i] = (in[i] & 0xff00) >> 8;
		temp[3][i] = in[i] & 0xff;
	}

	for (i = 0; i <= 3; i++)
	{
		s[0][i] = (x_mul(temp[0][i], 0xe)) ^ (x_mul(temp[1][i], 0xb)) ^ (x_mul(temp[2][i], 0xd)) ^ (x_mul(temp[3][i], 0x9));
		s[1][i] = (x_mul(temp[0][i], 0x9)) ^ (x_mul(temp[1][i], 0xe)) ^ (x_mul(temp[2][i], 0xb)) ^ (x_mul(temp[3][i], 0xd));
		s[2][i] = (x_mul(temp[0][i], 0xd)) ^ (x_mul(temp[1][i], 0x9)) ^ (x_mul(temp[2][i], 0xe)) ^ (x_mul(temp[3][i], 0xb));
		s[3][i] = (x_mul(temp[0][i], 0xb)) ^ (x_mul(temp[1][i], 0xd)) ^ (x_mul(temp[2][i], 0x9)) ^ (x_mul(temp[3][i], 0xe));
	}

	in[0] = (s[0][0] << 24) + (s[1][0] << 16) + (s[2][0] << 8) + s[3][0];
	in[1] = (s[0][1] << 24) + (s[1][1] << 16) + (s[2][1] << 8) + s[3][1];
	in[2] = (s[0][2] << 24) + (s[1][2] << 16) + (s[2][2] << 8) + s[3][2];
	in[3] = (s[0][3] << 24) + (s[1][3] << 16) + (s[2][3] << 8) + s[3][3];

	return;

}



void extendkey(int *w)
{//擴展密鑰
	//W[0]~W[3]的初始化
	int result[4];
	int i;
	for (i = 0; i <= 3; i++)
	{
		result[0] = (int)key[i * 4 + 0] & 0xff;
		result[1] = (int)key[i * 4 + 1] & 0xff;
		result[2] = (int)key[i * 4 + 2] & 0xff;
		result[3] = (int)key[i * 4 + 3] & 0xff;
		w[i] = (result[0] << 24) + (result[1] << 16) + (result[2] << 8) + result[3];
	}
	//擴展密鑰
	for (int i = 4, j = 0; i < 44; i++) 
	{
		int Rcon[10] = {/*0*/ 0x01000000, 0x02000000,
			/*1*/0x04000000, 0x08000000,
			/*2*/0x10000000, 0x20000000,
			/*3*/0x40000000, 0x80000000,
			/*4*/0x1b000000, 0x36000000 };
		if (i % 4 == 0)
		{
			int temp = w[i - 1];
			//循環左移
			int temp1 = (temp & 0xff000000) >> 24;
			temp = temp << 8;
			temp = temp + temp1;
			//字節替代
			int t[4];
			t[0] = (temp & 0xff000000) >> 24;
			t[1] = (temp & 0xff0000) >> 16;
			t[2] = (temp & 0xff00) >> 8;
			t[3] = temp & 0xff;
			t[0] = SubBytes(t[0]); t[1] = SubBytes(t[1]);
			t[2] = SubBytes(t[2]); t[3] = SubBytes(t[3]);
			temp = (t[0] << 24) + (t[1] << 16) + (t[2] << 8) + t[3];
			//與輪常量rc[n]相加
			temp = temp ^ Rcon[j];
			//異或
			w[i] = w[i - 4] ^ temp;
			j++;//下一輪
		}
		else
		{
			w[i] = w[i - 4] ^ w[i - 1];
		}
	}
}


void addround(int *w, int *in, int i)
{//輪密鑰加
	in[0] = in[0] ^ w[i];
	in[1] = in[1] ^ w[i + 1];
	in[2] = in[2] ^ w[i + 2];
	in[3] = in[3] ^ w[i + 3];
	return;
}



void beginin(int *in)
{//輸入的字符串轉化爲矩陣
	int result[4];
	int i;
	for (i = 0; i <= 3; i++)
	{
		result[0] = (int)input[i * 4 + 0] & 0xff;
		result[1] = (int)input[i * 4 + 1] & 0xff;
		result[2] = (int)input[i * 4 + 2] & 0xff;
		result[3] = (int)input[i * 4 + 3] & 0xff;
		in[i] = (result[0] << 24) + (result[1] << 16) + (result[2] << 8) + result[3];
	}
}

int main()
{
	S_ni();
	//freopen("in.txt", "r", stdin);
	printf("請輸入16個字符的待加密的明文:\n");
	scanf("%s", input);
	printf("請輸入16個字符的密鑰:\n");
	scanf("%s", key);

	int w[44];
	int in[4];

	printf("\n\n**************  加密中  ***************\n\n\n");
	//AES加密
	int i;
	extendkey(w);
	beginin(in);
	addround(w, in, 0);
	for (i = 1; i < 10; i++) 
	{//1-9輪

		translate(in);//字節代換
		lineshift(in);//行移位
		mixcolumn(in);//列混合
		addround(w, in, 4 * i);//輪密鑰加

	}
	//第10輪
	translate(in);//字節代換
	lineshift(in);//行移位
	addround(w, in, 40);//輪密鑰加

	//輸出密文信息

	printf("...... AES加密後的密文的ASCCI碼爲 ......\n\n");
	for (i = 0; i <= 3; i++)
	{
		int t[4];
		t[0] = (in[i] & 0xff000000) >> 24;
		t[1] = (in[i] & 0xff0000) >> 16;
		t[2] = (in[i] & 0xff00) >> 8;
		t[3] = in[i] & 0xff;
		printf("0x%2x 0x%2x 0x%2x 0x%2x ", t[0], t[1], t[2], t[3]);
		if (i == 1)printf("\n");
	}
	printf("\n........................................\n\n");
	printf(".......... AES加密後的密文爲 ...........\n\n");
	for (i = 0; i <= 3; i++)
	{
		int t[4];
		t[0] = (in[i] & 0xff000000) >> 24;
		t[1] = (in[i] & 0xff0000) >> 16;
		t[2] = (in[i] & 0xff00) >> 8;
		t[3] = in[i] & 0xff;
		printf("%c%c%c%c", t[0], t[1], t[2], t[3]);
	}
	printf("\n........................................\n");

	printf("\n\n\n**************  解密中  ***************\n\n");

	//AES解密
	addround(w, in, 40);//輪密鑰加
	//1-9輪
	for (i = 9; i >= 1; i--)
	{
		lineshift_ni(in);//逆行移位
		translate_ni(in);//逆字節代換
		addround(w, in, 4 * i);//輪密鑰加
		mixcolumn_ni(in);//逆列混合
	}
	//第10輪
	lineshift_ni(in);//逆行移位
	translate_ni(in);//逆字節代換
	addround(w, in, 0);//輪密鑰加
	
	
	//輸出解密後明文信息
	printf("\n\n...... AES解密後的明文的ASCCI碼爲 ......\n\n");
	for (i = 0; i <= 3; i++)
	{
		int t[4];
		t[0] = (in[i] & 0xff000000) >> 24;
		t[1] = (in[i] & 0xff0000) >> 16;
		t[2] = (in[i] & 0xff00) >> 8;
		t[3] = in[i] & 0xff;
		printf("0x%2x 0x%2x 0x%2x 0x%2x ", t[0], t[1], t[2], t[3]);
		if (i == 1)printf("\n");
	}
	printf("\n........................................\n\n");
	printf(".......... AES解密後的明文爲 ...........\n\n");
	for (i = 0; i <= 3; i++)
	{
		int t[4];
		t[0] = (in[i] & 0xff000000) >> 24;
		t[1] = (in[i] & 0xff0000) >> 16;
		t[2] = (in[i] & 0xff00) >> 8;
		t[3] = in[i] & 0xff;
		printf("%c%c%c%c", t[0], t[1], t[2], t[3]);
	}
	printf("\n........................................\n\n");

	return 0;
}

運行演示:

英文加密解密:

中文加密解密:

 

【小結和討論】

AES算法是高級加密標準,是一種對稱密碼,但是這一次的對稱密碼比以往的實驗都要複雜的多。我在這一次實驗上也花了很多的時間。

AES算法主要由四個不同的變換組成,包括一個置換和三個替代:字節代替,用一個S盒完成分組的字節到字節的代替;行移位,一個比較簡單的置換;列混淆,利用域GF(28)上的算術特性的一個代替;輪密鑰加,當前分組和擴展密鑰的一部分進行按位XOR(異或)。

我覺得這次實驗最難的一個功能模塊是列混淆。因爲首先列混淆的乘法不是一般的乘法,而是x乘法,即GF(2^8)上的乘法。其次這個模塊涉及到矩陣運算,我們知道矩陣運算是比較複雜,比較麻煩的。好在本次實驗矩陣的大小是4*4固定的,而且課程PPT中還給出了推導後的矩陣計算公式,使得我們不必要真的去寫一個x乘法矩陣運算,只需要套用一堆固定的公式,這帶來了一定程度上的方便,這裏我寫了x_mul、mixcolumn兩個函數來實現此功能,其中還要注意矩陣行、列計算的轉換,所以還是很複雜的,很考驗邏輯思維能力。

還有一個難點就是密鑰擴展,所以我藉此弄清楚了這其中的原理。把每輪的密鑰看成四個w[i],這一輪的第一個w[i]總是最難生成的。我們首先第n-1組第3列的4個字節循環左移1個字節;再對每個字節進行字節替代變換;再與輪常量rc[n]相加;最後再與前一組該列相加。這裏比較彎彎繞繞的,一不小心就會搞糊塗。

這次實驗很複雜、比較費時間,我前前後後花了差不多整整一天的時間才完成所有代碼的編寫和調試,一共寫了400多行代碼。經過這次實驗,感覺自己對AES算法的理解、對字符字節的控制、對整體代碼的debug上都有所進步,收穫良多。

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