AES加解密原理详解与算法实现

本文详细介绍了AES密码的实现过程,包括AES的加解密原理、核心操作如字节替代、行移位、列混淆和轮密钥加,并展示了C++代码实现。通过AES加密和解密示例,验证了算法的正确性,加深了对AES算法非线性、混淆和扩散特性的理解。在实践中,列混淆和密钥扩展作为难点,需要理解GF(2^8)上的运算以及矩阵变换。
摘要由CSDN通过智能技术生成
【AES密码编写】
1. Equipment

(1) operating system version :WIN 10

(2) CPU instruction set: x 64

(3) software :Visual Studio 2019

2. process
  • Problem background analysis

AES密码编写

​要编写AES算法,首先了解AES算法原理,AES算法是一个对称分组密码算法。数据分组长度必须是 128 bits,使用的密钥长度为 128,192 或 256 bits。对于三种不同密钥长度的 AES 算法,分别称为“AES-128”、“AES-192”、“AES-256”。AES加密算法涉及4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混(MixColumns)和轮密钥加(AddRoundKey)。

​ 从AES的加密和解密的流程图中可知:解密算法的每一步分别对应加密算法的逆操作。加解密所有操作的顺序正好是相反的,正是这样才保证了算法的正确性。加解密中每轮的密钥分别由种子密钥经过密钥扩展算法得到,算法中16字节的明文、密文和轮子密钥都以一个4x4的矩阵表示。

AES的加密和解密的流程如下:
在这里插入图片描述

​ 按照每一步的具体作用来编写对应的函数,以此实现AES算法过程。

  • Solution

code:

#include <stdio.h>
#pragma warning(disable:4996)
/*
 * @Author:timerring
 * @Date: 2021-11-08 21:59:11
 * @LastEditTime: 2021-11-12 23:44:19
 * @FilePath:c:\Users\timerring\AES.cpp
 */
 //定义轮常量表
static const unsigned char Rcon[10] = {
	0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36 };

//定义有限域*2乘法
static unsigned char x2time(unsigned char x)
{
	if (x & 0x80)
	{
		return (((x << 1) ^ 0x1B) & 0xFF);
	}
	return x << 1;
}
//定义有限域*3乘法
static unsigned char x3time(unsigned char x)
{
	return (x2time(x) ^ x);
}
//定义有限域*4乘法
static unsigned char x4time(unsigned char x)
{
	return (x2time(x2time(x)));
}
//定义有限域*8乘法
static unsigned char x8time(unsigned char x)
{
	return (x2time(x2time(x2time(x))));
}
//定义有限域*9乘法
static unsigned char x9time(unsigned char x)
{
	return (x8time(x) ^ x);
}
//定义有限域*B乘法
static unsigned char xBtime(unsigned char x)
{
	return (x8time(x) ^ x2time(x) ^ x);
}
//定义有限域*D乘法
static unsigned char xDtime(unsigned char x)
{
	return (x8time(x) ^ x4time(x) ^ x);
}
//定义有限域*E乘法
static unsigned char xEtime(unsigned char x)
{
	return (x8time(x) ^ x4time(x) ^ x2time(x));
}
//s盒矩阵 Substitution Table
static const unsigned char sbox[256] = {
	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 unsigned char contrary_sbox[256] = {
	0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,
	0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
	0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,
	0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
	0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,
	0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
	0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,
	0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
	0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,
	0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
	0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,
	0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
	0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,
	0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
	0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,
	0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
	0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,
	0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
	0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,
	0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
	0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,
	0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
	0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,
	0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
	0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,
	0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
	0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,
	0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
	0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,
	0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
	0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,
	0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d,
};
//定义列混合操作
static void MixColumns(unsigned char* col)
{
	unsigned char tmp[4], xt[4];
	int i;
	for (i = 0; i < 4; i++, col += 4)
	{  //col代表一列的基地址,col+4:下一列的基地址
		//xt[n]代表*2   xt[n]^col[n]代表*3   col[n]代表*1
		tmp[0] = x2time(col[0]) ^ x3time(col[1]) ^ col[2] ^ col[3];	//2 3 1 1
		tmp[1] = col[0] ^ x2time(col[1]) ^ x3time(col[2]) ^ col[3];	//1 2 3 1
		tmp[2] = col[0] ^ col[1] ^ x2time(col[2]) ^ x3time(col[3]);	//1 1 2 3
		tmp[3] = x3time(col[0]) ^ col[1] ^ col[2] ^ x2time(col[3]);	//3 1 1 2
		//修改后的值 直接在原矩阵上修改
		col[0] = tmp[0];
		col[1] = tmp[1];
		col[2] = tmp[2];
		col[3] = tmp[3];
	}
}
//定义逆向列混淆
static void Contrary_MixColumns(unsigned char* col)
{
	unsigned char tmp[4];
	unsigned char xt2[4];//colx2
	unsigned char xt4[4];//colx4
	unsigned char xt8[4];//colx8
	int x;
	for (x = 0; x < 4; x++, col += 4)
	{
		tmp[0] = xEtime(col[0]) ^ xBtime(col[1]) ^ xDtime(col[2]) ^ x9time(col[3]);
		tmp[1] = x9time(col[0]) ^ xEtime(col[1]) ^ xBtime(col[2]) ^ xDtime(col[3]);
		tmp[2] = xDtime(col[0]) ^ x9time(col[1]) ^ xEtime(col[2]) ^ xBtime(col[3]);
		tmp[3] = xBtime(col[0]) ^ xDtime(col[1]) ^ x9time(col[2]) ^ xEtime(col[3]);
		col[0] = tmp[0];
		col[1] = tmp[1];
		col[2] = tmp[2];
		col[3] = tmp[3];
	}
}
//定义行移位操作:行左循环移位
static void ShiftRows(unsigned char* col)
{//正向行移位
	unsigned char t;
	//左移1位
	t = col[1]; col[1] = col[5]; col[5] = col[9]; col[9] = col[13]; col[13] = t;
	//左移2位,交换2次数字来实现
	t = col[2]; col[2] = col[10]; col[10] = t;
	t = col[6]; col[6] = col[14]; col[14] = t;
	//左移3位,相当于右移1次
	t = col[15]; col[15] = col[11]; col[11] = col[7]; col[7] = col[3]; col[3] = t;
	//第4行不移位
}
//逆向行移位
static void Contrary_ShiftRows(unsigned char* col)
{
	unsigned char t;
	t = col[13]; col[13] = col[9]; col[9] = col[5]; col[5] = col[1]; col[1] = t;
	t = col[2]; col[2] = col[10]; col[10] = t;
	t = col[6]; col[6] = col[14]; col[14] = t;
	t = col[3]; col[3] = col[7]; col[7] = col[11]; col[11] = col[15]; col[15] = t;
	//同理,第4行不移位
}
//定义s盒字节代换替换操作
static void SubBytes(unsigned char* col)
{//字节代换
	int x;
	for (x = 0; x < 16; x++)
	{
		col[x] = sbox[col[x]];
	}
}
//逆向字节代换
static void Contrary_SubBytes(unsigned char* col)
{
	int x;
	for (x = 0; x < 16; x++)
	{
		col[x] = contrary_sbox[col[x]];
	}
}
//密钥编排,16字节--->44列32bit密钥生成--> 11组16字节:分别用于11轮 轮密钥加运算
void ScheduleKey(unsigned char* inkey, unsigned char* outkey, int Nk, int Nr)
{
	//inkey:初始16字节密钥key
	//outkey:11组*16字节扩展密钥expansionkey
	//Nk:4列
	//Nr:10轮round
	unsigned char temp[4], t;
	int x, i;
	/*copy the key*/
	//第0组:[0-3]直接拷贝
	for (i = 0; i < (4 * Nk); i++)
	{
		outkey[i] = inkey[i];
	}
	//第1-10组:[4-43]
	i = Nk;
	while (i < (4 * (Nr + 1))) //i=4~43 WORD 32bit的首字节地址,每一个4字节
	{//1次循环生成1个字节扩展密钥,4次循环生成一个WORD
		//temp:4字节数组:代表一个WORD密钥
		//i不是4的倍数的时候
		//每个temp = 每个outkey32bit = 4字节
		for (x = 0; x < 4; x++)
			temp[x] = outkey[(4 * (i - 1)) + x];	//i:32bit的首字节地址
		//i是4的倍数的时候
		if (i % Nk == 0)
		{
			/*字循环:循环左移1字节 RotWord()*/
			t = temp[0]; temp[0] = temp[1]; temp[1] = temp[2]; temp[2] = temp[3]; temp[3] = t;
			/*字节代换:SubWord()*/
			for (x = 0; x < 4; x++)
			{
				temp[x] = sbox[temp[x]];
			}
			/*轮常量异或:Rcon[j]*/
			temp[0] ^= Rcon[(i / Nk) - 1];
		}
		for (x = 0; x < 4; x++)
		{
			outkey[(4 * i) + x] = outkey[(4 * (i - Nk)) + x] ^ temp[x];
		}
		++i;
	}
}
//定义轮密钥加操作
static void AddRoundKey(unsigned char* col, unsigned char* expansionkey, int round)//密匙加
{
	//扩展密钥:44*32bit =11*4* 4*8 =  16字节*11轮,每轮用16字节密钥
	//第0轮,只进行一次轮密钥加
	//第1-10轮,轮密钥加
	int x;
	for (x = 0; x < 16; x++)
	{	//每1轮操作:4*32bit密钥 = 16个字节密钥
		col[x] ^= expansionkey[(round << 4) + x];
	}
}
//AES加密函数
void AesEncrypt(unsigned char* blk, unsigned char* expansionkey, int Nr)
{//加密一个区块
	//输入blk原文,直接在上面修改,输出blk密文
	//输入skey:
	//输入Nr = 10轮
	int round;
	//第1轮之前:轮密钥加
	AddRoundKey(blk, expansionkey, 0);
	//第1-9轮:4类操作:字节代换、行移位、列混合、轮密钥加
	for (round = 1; round <= (Nr - 1); round++)
	{
		SubBytes(blk);		//输入16字节数组,直接在原数组上修改
		ShiftRows(blk);		//输入16字节数组,直接在原数组上修改
		MixColumns(blk);	//输入16字节数组,直接在原数组上修改
		AddRoundKey(blk, expansionkey, round);
	}
	//第10轮:不进行列混合
	SubBytes(blk);
	ShiftRows(blk);
	AddRoundKey(blk, expansionkey, Nr);
}
//AES 解密函数
void Contrary_AesEncrypt(unsigned char* blk, unsigned char* expansionkey, int Nr)
{
	int x;
	AddRoundKey(blk, expansionkey, Nr);
	Contrary_ShiftRows(blk);
	Contrary_SubBytes(blk);
	for (x = (Nr - 1); x >= 1; x--)
	{
		AddRoundKey(blk, expansionkey, x);
		Contrary_MixColumns(blk);
		Contrary_ShiftRows(blk);
		Contrary_SubBytes(blk);
	}
	AddRoundKey(blk, expansionkey, 0);
}
int main(void) {
	unsigned char pt[17], key[17];
	//定义原文pt
	//定义密钥key
	unsigned char expansionkey[15 * 16];
	int i;
	int j;
	printf("You are welcome to use the AES machine in SDU\n");
	printf("Please enter plaintext (length = 16):\n");
	scanf("%s", pt);
	printf("please input key:\n");
	scanf("%s", key);

	//加密过程
	ScheduleKey(key, expansionkey, 4, 10);//密钥扩展生成
	AesEncrypt(pt, expansionkey, 10);//调用AES 加密
	printf("ciphertext: ");	
	//输出密文
	for (i = 0; i < 16; i++)
	{
		printf("%02x ", pt[i]);
	}
	printf("\n");
	printf("\n");

	//解密过程
	Contrary_AesEncrypt(pt, expansionkey, 10);//调用AES 解密
	printf("The decrypted plaintext is: ");
	//输出明文
	for (i = 0; i < 16; i++)
	{
		printf("%c ", pt[i]);
	}
	printf("\n");
	printf("\n");
	while (1);
	return 0;
}

Test sample:

​ 此处假定测试明文为SDUinQINGDAOcity,设置测试密钥为iloveSDUforeverr,进行加密得到密文,再对密文进行解密,经验证可以得到明文,AES程序编写成功。

image-20211219234445170


3. summary and harvest

我对AES密码的认识更加深入了,除了常见的加轮密钥以外,另外三个过程一起提供了混淆、扩散和非线性功能。这三个过程没有涉及密钥,就它们自身而言,并未提供算法的安全性。然而,该算法经历一个分组的XOR加密(轮密钥加),再对该分组混淆扩散(其他三个阶段),再接着又是XOR加密,如此交替进行,这种方式非常有效非常安全。其次,AES算法的每个阶段均可逆。对字节代替、行移位和列混淆,在解密算法中用它们相对应的逆函数。轮密钥加的逆就是用同样的轮密钥和分组相异或,其原理就是A⊕B⊕B = A。和大多数分组密码一样,AES解密算法按逆序利用扩展密钥,然而其解密算法和加密算法并不一样,这是由AES的特定结构决定的。

​ 而此次实验我印象最深的就是对于各轮AES加密循环(除最后一轮外)4个方法的编写:

  1. AddRoundKey — 矩阵中的每一个字节都与该次轮秘钥(round key)做XOR运算;每个子密钥由密钥生成方案产生。
  2. SubBytes — 通过非线性的替换函数,用查找表的方式把每个字节替换成对应的字节。
  3. ShiftRows — 将矩阵中的每个横列进行循环式移位。
  4. MixColumns — 为了充分混合矩阵中各个直行的操作。这个步骤使用线性转换来混合每列的四个字节。

​ 其中我觉得难处理的一个功能模块是列混淆。因为混淆变换实际上是使用乘法矩阵(其运算中涉及的加法和乘法都是定义在GF(28)上的加法和乘法,目的就是为了确保运算结果不会溢出定义域)。其次这个模块涉及到矩阵运算。好在本次实验矩阵的大小是4*4固定的,其中还要注意矩阵行、列计算的转换,所以还是很复杂的,很考验逻辑思维能力。

​ 还有一个难点就是密钥扩展。把每轮的密钥看成四个w[i],这一轮的第一个w[i]总是最难生成的。我们首先第n-1组第3列的4个字节循环左移1个字节;再对每个字节进行字节替代变换;再与轮常量相加;最后再与前一组该列相加。

​回顾起此AES密码编程,感慨颇多,从理论到实践,在这次的实验中,能够学到很多很多的东西,不仅仅巩固了课上所学过的AES知识,也学到了很多在书本上所没有学到过的知识。在实验操作与设计的过程中遇到问题也颇多,但可喜的是通过查找资料,阅读博客,查阅文献的方式,这些问题最终都得到了解决。

初学信息安全,可能存在错误之处,还请各位不吝赐教。

受于文本原因,本文相关算法实现工程无法展示出来,现已将资源上传,可自行点击下方链接下载。

AES加解密原理详解与算法实现工程文件

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timerring

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值