AES-128算法实现(附C++源码)

AES-128算法实现(附C++源码)

前言

这次写AES算法的实验,发现还是比较麻烦的。同时,身边的很多同学对于该算法的编程实现会觉得比较晦涩,再加上AES本身的算法本身也比较复杂,使得实现起来比较麻烦,除此之外,很多同学也反映出网上的很多源码的程序都不能跑。基于这些情况,我决定出这么的一篇blog,希望能够帮助你们理解AES算法并完成对应的编程实现。

AES的算法流程

不妨参考下图

在这里插入图片描述

或者参考这个来自课件的流程图,会更清楚

在这里插入图片描述

如你所见,AES的流程会分为几部分:密钥拓展轮密钥加字节代换行位移列混淆。 其中后面四部分会出现在迭代轮数中,也在解密中 会有对应的 逆操作

几个你会用到的常量

S盒
// 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盒
// 逆s 盒, 用于在解密时的 逆字节变换
static const int S1[16][16] = { 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 };

常量轮值表

与网上其他大大的不同,我采用了long long 存储,主要是为了防止超限。

// 常量轮值表
// 这里将原来的复制粘贴过来的轮常量类别改成了 long long
// 原因在于第8位超出int限,数据溢出
static const long long Rcon[10] = { 
	0x01000000, 0x02000000,
	0x04000000, 0x08000000,
	0x10000000, 0x20000000,
	0x40000000, 0x80000000,
	0x1b000000, 0x36000000 
};

密钥拓展

密钥拓展的主要是需要先将现有的密钥进行分组,这里是分成了四组,然后进行40次拓展,我们假设的原始的分好组的占最终的 keys[0:3], 对于任意的 i >= 4, i < 44, 有:
k e y s [ i ] = { k e y s [ i − 1 ] ⊕ k e y s [ i − 4 ] , i % 4 ! = 0 T ( k e y s [ i − 1 ] ) ⊕ k e y s [ i − 4 ] , i % 4 = = 0 keys[i] = \left\{ \begin{aligned} keys[i - 1] \oplus keys[i - 4], \qquad i \% 4 != 0 \\ T(keys[i-1]) \oplus keys[i - 4], \qquad i \% 4 == 0 \end{aligned} \right. keys[i]={keys[i1]keys[i4],i%4!=0T(keys[i1])keys[i4],i%4==0
T函数是一个复杂函数,涉及到字循环、字节代换、轮常量异或。

vector<string> extend_key(string& key)
{
	// 先分组
	vector<string> w_key = group_key(key);
	for (int i = 0; i < 40; ++i)
	{
		string w = "";
		int index = 4 + i;
		string temp = w_key[index - 1];
		// 4 的倍数的时候,需要调用T函数
		if (index % 4 == 0)
		{
			temp = T(temp, index / 4 - 1);
		}
		w = string_xor(temp, w_key[index - 4]);

		// 压入数组中
		w_key.push_back(w);
	}

	return w_key;
}

轮密钥加

该部分其实在不同的轮数中,将密文或者明文与对应密钥部分做异或操作。我使用的是字符串存储,所以也做了字符串异或的操作。

// 一开始的先进行一次轮密钥加
vector<string> texts = group_key(plain_text);
for (int i = 0; i < 4; ++i)
{
texts[i] = string_xor(texts[i], keys[i]);
}
// 写一个字符串对应的16进制数异或的函数
string string_xor(string s1, string s2)
{
	long long num1 = str_long(s1), num2 = str_long(s2);
	long long num = num1 ^ num2;
	// 再把数字转为字符串
	string ans = int_to_chs(num);
	// 不足8位的时候补位
	while (ans.length() < 8)
	{
		ans = "0" + ans;
	}
	return ans;
}

字节代换

其实就是用当前的坐标找到S盒或者逆S盒对应的替换值,先将字符转成数值坐标,再利用坐标替换,然后再转换成数值即可。

// 字节代换 的实现
string wordbyte_sub(string& wi_1)
{
	int len = wi_1.length();
	string ans = "";
	for (int i = 0; i < len; i += 2)
	{
		// 先获取当前的下标
		int x = ch_to_int(wi_1[i]), y = ch_to_int(wi_1[i + 1]);
		// 然后获取当前的数字
		int num = S[x][y];
		// 先将数值转化为字符串
		string s = int_to_chs(num);
		// 然后不足的话补0
		while (s.length() < 2)
		{
			s = "0" + s;
		}

		// 加起来
		ans += s;
	}
	return ans;
}

行移位

其实就是根据在矩阵中第几行,然后循环左移或者右移多少位。

由于我是采用字符串数组实现,所以这里的行移位其实对应到代码中其实是列移位。

// 行移位函数
vector<string> move_row(vector<string>& s)
{
	vector<string> ans = s;
	// 几个比较麻烦的地方
	// 我的字符串数组其实每个是对应一列, 所以其实是对应到列进行移位
	// 一行对应有两个16进制数,所以需要两个一起移动,其实就是对应两列一起动
	for (int i = 0; i < 4; ++i)
	{
		int k = i * 2;
		// 就原本矩阵对应的行移位,对于字符串数组就是列移位
		for (int j = 0; j < 4; ++j)
		{
			ans[j][k] = s[(j + i) % 4][k];
			ans[j][k + 1] = s[(j + i) % 4][k + 1];
		}
	}
	return ans;
}

列混淆

这一部分需要将矩阵与另外一个矩阵相乘,不过这里有一个简单的转换公式。

在这里插入图片描述

上面是逆变换操作,则是分别对应 ×e,b,d,9.

和行移位类似,这里的列混淆在代码中其实就是一个行操作。

除此之外,在操作的时候,这些乘法不妨将其拆解成2的i次幂,这样我们可以在 GF(2^8)上直接进行移位操作。

// 接下来就是列混淆
vector<string> col_confuse(vector<string>& s)
{
	vector<string> ans = s;
	// 算法中对应的是列,这边就直接变成了行,即字符
	for (int i = 0; i < 4; ++i)
	{
		// 需要先将字符串拆分成两两一组,共4组
		auto temp = split_s(s[i]);
		// 先转成数字
		int s0 = str_long(temp[0]), s1 = str_long(temp[1]), s2 = str_long(temp[2]), 
			s3 = str_long(temp[3]);
		// 计算混淆后的值
		int t0 = power(s0) ^ power(s1) ^ s1 ^ s2 ^ s3;
		int t1 = s0 ^ power(s1) ^ power(s2) ^ s2 ^ s3;
		int t2 = s0 ^ s1 ^ power(s2) ^ s3 ^ power(s3);
		int t3 = s0 ^ power(s0) ^ s1 ^ s2 ^ power(s3);
		// 转换成字符串再相加
		ans[i] = int_ch2(t0) + int_ch2(t1) + int_ch2(t2) + int_ch2(t3);
	}
	return ans;
}

完整源码(C++)

输入和输出

输入:

0
0123456789abcdeffedcba9876543210
0f1571c947d9e8590cb7add6af7f6798

输出:

ff0b844a0853bf7c6934ab4364148fb9

输入:

1
ff0b844a0853bf7c6934ab4364148fb9
0f1571c947d9e8590cb7add6af7f6798

输出:

0123456789abcdeffedcba9876543210
#include<iostream>
#include<string>
#include<vector>

using namespace std;

// 用于只选取低8bit
#define MOD 256

// 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 S1[16][16] = { 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 };

// 常量轮值表
// 这里将原来的复制粘贴过来的轮常量类别改成了 long long
// 原因在于第8位超出int限,数据溢出
static const long long Rcon[10] = { 
	0x01000000, 0x02000000,
	0x04000000, 0x08000000,
	0x10000000, 0x20000000,
	0x40000000, 0x80000000,
	0x1b000000, 0x36000000 
};

// 将字符转整数
int ch_to_int(char& ch);

// 将16进制字符串转为数字
long long str_long(string str)
{
	long long ans = 0;
	// 遍历字符串,将字符串的内容变为16进制数字
	for (char ch : str)
	{
		ans = ans * 16 + ch_to_int(ch);
	}
	return ans;
}

// 出于方便考虑,还是写一个字符转成数字的函数
int ch_to_int(char& ch)
{
	int ans = 0;
	// 数字的时候
	if (ch >= 48 && ch <= 57)
	{
		ans = ch - '0';
	}
	// 16进制中a 到 f
	else if (ch >= 'a' && ch <= 'f')
	{
		ans = ch - 'a' + 10;
	}
	// 这个在本实验的样例中,其实不会运行到的
	else if (ch >= 'A' && ch <= 'F')
	{
		ans = ch - 'A' + 10;
	}
	return ans;
}

// 然后再写一个数字转16进制字符串的吧,只考虑小写吧
// 针对的其实是单个16进制
string int_to_chs(long long num)
{
	string ans = "";
	while (num)
	{
		// 通过位运算得到低四位
		int x = num & 0xf;
		// 根据数值进行区分
		if (x <= 9)
		{
			char ch = x + '0';
			ans += ch;
		}
		else
		{
			char ch = x - 10 + 'a';
			ans += ch;
		}
		// 移位,其实相当于 / 16
		num >>= 4;
	}
	// 然后反转字符
	int left = 0, right = ans.length() - 1;
	// 双指针实现字符串反转
	while (left < right)
	{
		char  ch = ans[left];
		ans[left] = ans[right];
		ans[right] = ch;
		left++;
		right--;
	}
	return ans;
}

// 得先分组
vector<string> group_key(string& key)
{
	// 四组
	vector<string> groups(4);
	// 初始下标
	int index = 0;
	// 分组
	for (string& g : groups)
	{
		g = key.substr(index, 8);
		index += 8;
	}
	return groups;
}

// 字节循环
string loop_wordbyte(string& wi_1);

// 字节代换
string wordbyte_sub(string& wi_1);

// 轮常量异或
string xor_with_const(string& wi_1, int rounds);


// 拓展的时候,下标为4 的倍数时,需要麻烦一些,需要使用一个变换的T函数
string T(string& wi_1, int round)
{
	// T 变换由3部分构成, 所以可以再写三个函数
	// 先进行字循环
	string ans = loop_wordbyte(wi_1);
	// 然后字节代换
	ans = wordbyte_sub(ans);
	// 最后是轮异或
	ans = xor_with_const(ans, round);

	return ans;
}

// 字节循环实现
string loop_wordbyte(string& wi_1)
{
	string ans = wi_1.substr(2) + wi_1.substr(0, 2);
	return ans;
}

// 字节代换 的实现
string wordbyte_sub(string& wi_1)
{
	int len = wi_1.length();
	string ans = "";
	for (int i = 0; i < len; i += 2)
	{
		// 先获取当前的下标
		int x = ch_to_int(wi_1[i]), y = ch_to_int(wi_1[i + 1]);
		// 然后获取当前的数字
		int num = S[x][y];
		// 先将数值转化为字符串
		string s = int_to_chs(num);
		// 然后不足的话补0
		while (s.length() < 2)
		{
			s = "0" + s;
		}

		// 加起来
		ans += s;
	}
	return ans;
}

// 轮常量异或
string xor_with_const(string& wi_1, int rounds)
{
	// 先将字符串变为数字
	long long num = 0;
	for (int i = 0; i < 8; ++i)
	{
		char ch = wi_1[i];
		num = num * 16 + ch_to_int(ch);
	}
	// 计算异或结果
	num ^= Rcon[rounds];

	// 将num转化为字符串
	string res = int_to_chs(num);
	while (res.length() < 8)
	{
		res = "0" + res;
	}
	return res;
}

// 写一个字符串对应的16进制数异或的函数
string string_xor(string s1, string s2)
{
	long long num1 = str_long(s1), num2 = str_long(s2);
	long long num = num1 ^ num2;
	// 再把数字转为字符串
	string ans = int_to_chs(num);
	// 不足8位的时候补位
	while (ans.length() < 8)
	{
		ans = "0" + ans;
	}
	return ans;
}


// 先要进行密钥的拓展
vector<string> extend_key(string& key)
{
	// 先分组
	vector<string> w_key = group_key(key);
	for (int i = 0; i < 40; ++i)
	{
		string w = "";
		int index = 4 + i;
		string temp = w_key[index - 1];
		// 4 的倍数的时候,需要调用T函数
		if (index % 4 == 0)
		{
			temp = T(temp, index / 4 - 1);
		}
		w = string_xor(temp, w_key[index - 4]);

		// 压入数组中
		w_key.push_back(w);
	}

	return w_key;
}

// 行移位函数
vector<string> move_row(vector<string>& s)
{
	vector<string> ans = s;
	// 几个比较麻烦的地方
	// 我的字符串数组其实每个是对应一列, 所以其实是对应到列进行移位
	// 一行对应有两个16进制数,所以需要两个一起移动,其实就是对应两列一起动
	for (int i = 0; i < 4; ++i)
	{
		int k = i * 2;
		// 就原本矩阵对应的行移位,对于字符串数组就是列移位
		for (int j = 0; j < 4; ++j)
		{
			ans[j][k] = s[(j + i) % 4][k];
			ans[j][k + 1] = s[(j + i) % 4][k + 1];
		}
	}
	return ans;
}

// 写个函数分割一下字符串,长度为8变4组
vector<string> split_s(string& s)
{
	vector<string> ans;
	for (int i = 0; i < s.length(); i += 2)
	{
		ans.emplace_back(s.substr(i, 2));
	}
	return ans;
}

string int_ch2(int num)
{
	string ans = int_to_chs(num);
	// 这里是为了确保只有两位字符串
	while(ans.length() < 2)
	{
		ans = "0" + ans;
	}
	return ans;
}

// 移位函数,其实就是在GF(2^8)的范围进行幂次操作
int power(int num)
{
	int ans = (num << 1) % MOD;
	// 如果第七位是1
	if (num & 0x80)
	{
		ans ^= 0x1b;
	}
	return ans;
}

// 接下来就是列混淆
vector<string> col_confuse(vector<string>& s)
{
	vector<string> ans = s;
	// 算法中对应的是列,这边就直接变成了行,即字符
	for (int i = 0; i < 4; ++i)
	{
		// 需要先将字符串拆分成两两一组,共4组
		auto temp = split_s(s[i]);
		// 先转成数字
		int s0 = str_long(temp[0]), s1 = str_long(temp[1]), s2 = str_long(temp[2]), 
			s3 = str_long(temp[3]);
		// 计算混淆后的值
		int t0 = power(s0) ^ power(s1) ^ s1 ^ s2 ^ s3;
		int t1 = s0 ^ power(s1) ^ power(s2) ^ s2 ^ s3;
		int t2 = s0 ^ s1 ^ power(s2) ^ s3 ^ power(s3);
		int t3 = s0 ^ power(s0) ^ s1 ^ s2 ^ power(s3);
		// 转换成字符串再相加
		ans[i] = int_ch2(t0) + int_ch2(t1) + int_ch2(t2) + int_ch2(t3);
	}
	return ans;
}

// 写一个测试函数
void show(vector<string>& text)
{
	for (auto t : text)
	{
		cout << t;
	}
	cout << endl;
}

// 先定义一个 aes 加密函数
void aes(string& plain_text, string& key)
{
	// 先拓展密钥
	vector<string> keys = extend_key(key);

	int index = 0;
	// 然后就是10轮迭代
	// 需要知道明文其实是32位,所以需要搞4下
	// 可以先把明文也分组

	// 一开始的先进行一次轮密钥加
	vector<string> texts = group_key(plain_text);
	for (int i = 0; i < 4; ++i)
	{
		texts[i] = string_xor(texts[i], keys[i]);
	}
	index += 4;
	// 然后十次迭代
	for (int k = 0; k < 10; ++k)
	{
		for (int j = 0; j < 4; ++j)
		{
			// 先是字节代换
			texts[j] = wordbyte_sub(texts[j]);
		}
		// 然后是行移位
		texts = move_row(texts);

		if (k < 9)
		{
			// 再来列混淆
			texts = col_confuse(texts);
		}

		// 轮密钥加
		for (int i = 0; i < 4; ++i)
		{
			texts[i] = string_xor(texts[i], keys[i + index]);
		}

		index += 4;
	}
	string ans = "";
	for (int i = 0; i < 4; ++i)
	{
		ans += texts[i];
	}
	cout << ans << endl;
}

// 行移位的逆操作函数
vector<string> in_move_row(vector<string>& s)
{
	vector<string> ans = s;
	// 现在变成了逆操作
	for (int i = 0; i < 4; ++i)
	{
		int k = i * 2;
		// 就原本矩阵对应的行移位,对于字符串数组就是列移位
		for (int j = 0; j < 4; ++j)
		{
			ans[j][k] = s[(j - i + 4) % 4][k];
			ans[j][k + 1] = s[(j - i + 4) % 4][k + 1];
		}
	}
	return ans;
}

// 逆字节代换
string in_wordbyte_sub(string& wi_1)
{
	int len = wi_1.length();
	string ans = "";
	for (int i = 0; i < len; i += 2)
	{
		// 先获取当前的下标
		int x = ch_to_int(wi_1[i]), y = ch_to_int(wi_1[i + 1]);
		// 然后获取当前的数字
		int num = S1[x][y];
		// 先将数值转化为字符串
		string s = int_to_chs(num);
		// 然后不足的话补0
		while (s.length() < 2)
		{
			s = "0" + s;
		}

		// 加起来
		ans += s;
	}
	return ans;
}


// 列混淆的逆变换
vector<string> in_col_confuse(vector<string>& s)
{
	// 逆变换其实原来的变换矩阵的逆矩阵,对应0xe, 0xb, 0xd, 0x9
	// 4 列

	vector<string> ans = s;
	for (int i = 0; i < 4; ++i)
	{
		// 先分割成4个两位数字
		auto temp = split_s(s[i]);
		// 转换成数字
		vector<int> nums(4);
		for (int j = 0; j < 4; ++j)
		{
			nums[j] = str_long(temp[j]);
		}
		vector<int> t4(4, 0);
		for (int j = 0; j < 4; ++j)
		{
			for (int t = 0; t < 4; ++t)
			{
				int k = (t - j + 4) % 4;
				t4[j] ^= power(power(power(nums[t])));		// 表示8
				switch (k)
				{
				case 0:		// 0xe = 8 + 4 + 2
				{
					t4[j] ^= power(power(nums[t])) ^ power(nums[t]);
					break;
				}
				case 1:		// 0xb = 8 + 2 + 1
				{
					t4[j] ^= power(nums[t]) ^ nums[t];
					break;
				}
				case 2:		// 0xd = 8 + 4 + 1
				{
					t4[j] ^= power(power(nums[t])) ^ nums[t];
					break;
				}
				default:	// 0x9 = 8 + 1
					t4[j] ^= nums[t];
					break;
				}

			}
		}
		// 将数字转换成字符串存储
		ans[i] = int_ch2(t4[0]) + int_ch2(t4[1]) + int_ch2(t4[2]) + int_ch2(t4[3]);
	}

	return ans;
}

// 现在写一个 aes 解密函数
void in_aes(string& text, string& key)
{
	// 先拓展密钥
	auto keys = extend_key(key);

	// 初始下标
	int index = 40;

	// 对密文分组
	vector<string> texts = group_key(text);
	
	// 一开始先进行依次轮密钥加
	for (int i = 0; i < 4; ++i)
	{
		texts[i] = string_xor(texts[i], keys[index + i]);
	}
	index -= 4;

	// 然后十次迭代
	for (int i = 0; i < 10; ++i)
	{
		// 先逆行移位
		texts = in_move_row(texts);

		// 然后是字节代换逆操作
		for (int j = 0; j < 4; ++j)
		{
			texts[j] = in_wordbyte_sub(texts[j]);
		}

		// 轮密钥加
		for (int j = 0; j < 4; ++j)
		{
			texts[j] = string_xor(texts[j], keys[index + j]);
		}

		// 除了最后一轮,都要列混淆逆变换
		if (i < 9)
		{
			texts = in_col_confuse(texts);
		}
		index -= 4;
	}

	show(texts);
}

int main(int argc, char** argv)
{
	int op;
	cin >> op;			// 输入标识符
	string text, key;
	cin >> text >> key;		// 输入明文或者密文, 以及初始密钥
	// 根据标识符,决定加密还是解密
	if (op == 0)
	{
		aes(text, key);
	}
	else
	{
		in_aes(text, key);
	}

	return 0;
}

优化

对于这份代码我深知其实存在不少可以优化的地方。

比如:

  1. 字符串数组的存储并不方便,需要频繁切换,可以直接采用整数存储,最终输出时再用字符串输出。
  2. 用于采用矩阵存储,其实可以将轮常量只选取前两位即可。
  3. 可以在迭代时,完成对密钥的拓展,这样可以节省空间,当然只是对于加密,解密还是要先拓展。

后话

学会了吗?没学会就码起来,别老是当cv战神。

用C语言实现128AES加密算法,可以运行在JAVA的JNI 中AIS加密算法c语言实现代码 nt cnt for(ent =0: cnt< 8: cnt++) BvtcToBit(*(ch+cnt), bit+(ent<<3)) return /将二进制位串转为长度为8的字符串水 int Bit64ToChar8 (ElemType bitL64, ElemType ch18) int cnt memset(ch, 0, 8) for(ent-0: cnt<8: cnt++i BitToByte(bit+(cnt<<3), ch+cnt) return 0 /*生成子密钥 int DES Make Subkeys(ElemType key _64, ElemType subkeys [16][48]) ElemType temp 56 int cnt DES PCI Transform(key,temp):/*PCI置换* for(cnt=0;cnt<16;cnt+-){*16轮跌代,产生16个子密钥米 DES ROL(tenp, MOVE TIMES[cnt]);循坏左移* DES PC2 Transform(temp, subkeys cnt]);/PC2置换,产生子密钥体 return o /*密钥置換1*/ int DES PCI Transform(ElemType key [64, ElemType tempts[56])t int cnt for(cnt=0: cnt( 56 cnt++) )empts[cnt]= key[ Ilant] r巳turn /*密钥置換2* int DES PC2 Transform(Elem Type key [56], ElemType tempts[48])i t cnt for(ent =0: cnt< 48: cnt+)I )pbts [cnt]= key [PC 2[cnt]] return /*循环左移*/ int DES ROL (Elem Type data[56], int time)t Elem l'ype temp _56 /*保存将要循环栘动到右边的位* memcpy(temp, data, time) memcpy(temg-time, data+28, time) /*前28位移动 (data-28-time, temp, time) /*后28位移动* memcpy(data 28, data+28+time, 28-time memcpy (data-56-time, temp+time, time) return o /*P置换*/ int DES IP) Iransform(Elemlype data[64)[ ElemType temp _64]: for(cnt templet- datalIP Tablelcnt」」 memcpy(data, temp, 64) return o 第3页 AIS加密算法c语言实现代码 /*IP逆置換* int DES IP 1 Transform(ElemType data[64)( int cnt ElemType temp _64 for(cnt =0: cnt 64: cnt+-)i templet」- dataLIP1 Tablelcrt]」 memcpy(data, temp, 64) return o /*扩展置换*/ int DES E Transform(ElemType data[48])( Int cn ElemType temp48」 for(ent-0: cnt 48: cnt-) temp lent= datale Tablelent memcpy( data, temp, 48 return o P置换 int DES P Transform(ElemType data[32])( t ElemType temp_32] for(ent =0; cnt 32; cnt+-) temp ent-datalP Tablel 11 me.mcpy(data, temp, 32) return 0 /水异或* int DES XOR(Elem Type R[48, Elem Type L[48], int count)I int cnt for(cnt-0: cnt< count: cnt++)i RIant]= lent] return 0 /*S盒置换*/ int DES SBOX (Elem Type data[48]) int cnt int line, row, output int curl, cur for(ent=0; cnt( 8: cnt++i curl cnt:6 cur2= cnt<<2 /*计算在S盒中的行与列来 line=(data cur1<<1)+ data[ cur1+5 row=(data[cur1+1]<<3)+(data[cur1+2]<<2) +(data「cur1-31<<1)+data「cur1+41 output s[cnt][line]trow] /*化为2进制*/ data[cur2]=(output&0X08)>>3 data[cur2+1]=(output&0X04)>>2 data (output&0X02)>1 datalcur 2+3= output&0x01 return o 交换 int DES Swap(ElemType left[32], ElemType right [32]) memcpy(temp, left, 32 memcpy(left, right, 32 memcpy (right, temp, 32 return o 第4页 AIS加密算法c语言实现代码 /*加密单个分组 int DES EncryptBlockElem Type plainBlock[8, ElemType subKeys[l6][48, ElemType cipherBlock[8])I ElemT'ype plainTs [54] ElemType copyRight[48] int cnt Char8ToBit64(plainBlock, plairBits) /米初始置换(IP置换)* DES IP Transform(plainBits /*16轮迭代* for(cnt=0: cnt< 16: cnt+-) memcpy(copyRight, plainBits- 32, 32 /*将右半部分进行扩展置换,从32位扩展到48位*/ DES E Trans form(copyRight) /*将右半部分与子密钥进行异或操作 DES XOR (copy Righ 48) /*异或结果进入S盒,输出32位结果*/ DES SBOX (copyRight) /P置换 DES P Transform(copyRight) /*将明文左半部分与右半部分进行异或* DES XOR (plainBits, copyRight, 32) 最终完成左右部的交换* DES Swap(plainBits, plainBits-32) /逆初始置换(IPI置换)* DES IP 1 Transform (plainBits) Bit64ToChar8(plainBits, cipherBlock) turn o /*解密单个分组 int DES DecryptBlock(ElemType cipherBlock[8, ElemType subKeys[16] 18], ElemType plainBlock [81) ElemType cipherBits[ 641 Elem Type copy Right [48] int cnt Char8ToBit64(cipherBlock, cipherBits) /初始置换(IP置换)* DES IP Transform(cipherBits /*16轮迭代*/ for(cnt-15: cnt >-0: cnt--)i memcpy(copyRight, cipherBits+32, 32 /*将右半部分进行扩展置换,从32位扩展到48位 DES T Trans form(copyright) /*将右半部分与子密钥进行异或操作 DES XOR(copy Right, subKeys [ent], 48) /*异或结果进入S盒,输出32位结果* DES SBOX(copyRight) /米P置换* DES P Transform(copyright) /*将明文h半部分与右半部分进行异或* DES XOR (cipherBits, copy Right, 32) f(cnt /米最终完成左右部的交换* DES Swap(cipherBits, cipherBits+32) /*逆初始置换(IP1置换)* DES IP 1 Transform(cipherBits) Bit64ToChar8(cipherBits, plainBlock) return 0: *加密文件 int DES Encrypt (char xplainFile, char *keyStr, char *cipherFile)t FILE xplain, i*cipher; int count ElcmType plainBlock[81, ciphcrBlock [8, keyBlock 8 第5页 AIS加密算法c语言实现代码 Elem Type bEy 64] ElemType subKeys[16][18] if((plain- fopen(plainFilc, " rb"))--NULL) return Plain FILe OPEN ERROR return CIPHER FILE OPEN ERROR: ))== NULL)( if ((cipher fopen(cipherFile, wb /设置密钥 memcpy (keyBlock, key Str, 8) 将密钥转换为二进制流* Char8ToBit64(keyBlock, bKcy /牛成子密钥* DES Make SubKeys(bEy, subKeys while(!feof plain))( /*每次读8个字节,并返回成功读取的字节数* if((count- fread(plainBlock, sizeof(char),8, plain)) 8){ DES EncryptBlock (plainBlock, subKeys, cipherBlock f(count)[ /*填充*/ memset(plainBlock ount, \0, 7- count) /*最后一个字符休存包括最后一个字符在内的所填充的字符数量水 plainblockl7-8-count DES EncryptBlock (plainBlock, subkeys, cipherBlock fwrite(cipherBlock, sizeof (char), 8, cipher) fclose (plain) f'c. lose(cipher return oK /*解密文件* int DES Decrypt(char *cipherFile, char *key Str, char xplainFile)i FILE* plain,米 cipher int count, times 0 long fileLen Eleml'ype plainBlock [8], cipherBlock[8], keyBlock[8 ElemType bEy _6 ElemType subKeys[16][48] if ((cipher fopen(cipherFile, rb ))= NULL)[ return CIPHEr FILe OPEN ERROR if((plain= fopen(plainFile, wb" ))= NULL) rcturn plain FIle OPEN ERROR /*设置密钥* memcpy(key Block, keyStr, 8) /将密钥转换为二进制流* Char8ToBit64 (keyBlock, bKey) /水生成子密钥* ES Make SubKeys(bKey subKeys) /取文什长度*/ fseek( cipher,0, SEEK END);/将文件指针置尾 fi lelen= ftel l( cipher);/*取文件指针当前位置*/ rewind( CIpher);/*将文件指针重指向文件头* while(1)i 密文的字节数一定是8的整数倍* fread(cipherBlock, sizeof(char), 8, cipher DES DecryptBlock(cipherBlock, subKeys, plainBlock) times +-8 if(times< fileLen fwrite(plainBlock, sizeof(char), 8, plain) /*判断末尾是否被填充米 if(plainBlock 71< 8)i 第6页 AIS加密算法c语言实现代码 for(count=8- plainBlock[7]; count< 7; count++)( if(plainBlock[ count!='\0i break if( count==7){/*有填充* fwrite(plairBlock, sizeof (char), 8- plainBlockL7, plain) else{/*无填充 fwrite(plainBlock, sizeof(char), 8, plain) t'close ( plain) fclose(cipher) return OK int main() clock t a, b a= clockO DES Encrypt( 1. txt, key. txt, 2. txt b=clock o printf("加密消耗%毫秒Ⅶn",b-a); system("pause") a= clock( DES Decrypt( 2. txt, key. txt", 3. txt") printf("解密消耗%毫秒、n",o-a) getcharo return 第7页
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值