AES算法基于FPGA的硬件实现(2)AES算法的c++实现(ecb/cbc)

对于cpp来说内部有一些加密函数库来简单实现一些加密算法可以,但是为了更好理解内部实现流程,实现过程不调用cpp的库。
工程中出现的byte_t为bitset<8>类型,word为bitset<32>类型。base64转换文件为在网上找到的开源代码,在GitHub链接中有。
整体工程代码在末尾GitHub链接。

总体功能

  1. 实现了128/192/256三种密钥长度的ecb/cbc加密;
  2. 密钥输入可以少于实际要求输入,比如输入要求128bit但是少于128bit仍会填充;
  3. 实现了任意输入长度的明文输入(填充模式为pkcs7,可以大于小于128bit);
  4. 实现了base64的编码解码方便观察结果;
    在这里插入图片描述
    aes.h文件中有一些宏定义根据需要调整,明文长度只能固定为16,密钥长度24对应192bit模式。

ecb模式

1. 基本介绍

ecb模式是aes加密中最为简单的一个模式,该模式严格按照aes加密标准来执行加密(如下图)。
image.png|500

  • 这个模式最大的优点是可以并行执行,因为前后明文密文互不干扰,所以这种加密方式是最快的执行的。
  • 但是因为相同的明文会产生相同的密文,所以安全性比较差(效果如下图)。
    image.png|500

2.cpp实现

1. 密钥拓展函数

由加密原理那一部分可以看到加密首先要获得所有轮的轮密钥,所以要先进行密钥拓展。代码如下:

void aes::KeyExpansion(string strKey,word exp_key[4*(Nr+1)])
{
	unsigned char init_key[KEYCODELENGTH];
	byte_t k1,k2,k3,k4;

	KeyStringToHex( strKey.c_str(), init_key );//输入字符串转hex函数(同时对长度不够的密钥进行拼接)
	for(int i=0; i< 4*(Nr+1); ++i)//*循环4*(Nr+1)次,当为128bit的时候,循环44轮
	{
		if (i < Nk) //*前4个字直接赋值
		{
			k1 = (byte_t)init_key[4*i];
			k2 = (byte_t)init_key[4*i+1];
			k3 = (byte_t)init_key[4*i+2];
			k4 = (byte_t)init_key[4*i+3];
			exp_key[i] = byte2Word(k1, k2, k3, k4);
		}
		else if (i >= Nk && i%Nk == 0) //*发生为Nk倍数的情况
		{
			word temp = exp_key[i-1];
			temp = T(temp, i/Nk);
			exp_key[i] = temp ^ exp_key[i-Nk];
		}
		else if (i >= Nk && i%Nk == 4 && Nk == 8)//*只有aes256才会发生
		{
			exp_key[i] = SubWord(exp_key[i-1]) ^ exp_key[i-Nk];
		}
		else if(i > Nk)//*i > 4
		{
			exp_key[i] = exp_key[i-1] ^ exp_key[i-Nk];
		}
	}
}

2. 字节替代函数:使用s盒替代原有字节

const byte_t S_Box[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盒变换 - 前4位为行号,后4位为列号
 */
void aes::SubBytes(byte_t mtx[4*4])
{
	for(int i=0; i<16; ++i)
	{
		int row = mtx[i][7]*8 + mtx[i][6]*4 + mtx[i][5]*2 + mtx[i][4];
		int col = mtx[i][3]*8 + mtx[i][2]*4 + mtx[i][1]*2 + mtx[i][0];
		mtx[i] = S_Box[row][col];
	}
}

3. 行位移函数

/**
 * @description: rows shift
 * @param {byte_t} state:plain text
 * @return {*}
 */
void aes::ShiftRows(byte_t state[4*4])
{
	word temp;
	for (int i = 0; i < 4; i++)
	{
		temp = byte2Word(state[i*4],state[i*4+1],state[2+i*4],state[3+i*4]);
		temp = (temp << i*8) | (temp >> (32-i*8));
		state[i*4]   = (byte_t)(temp.to_ulong() >> 3*8);
		state[i*4+1] = (byte_t)(temp.to_ulong() >> 2*8);
		state[i*4+2] = (byte_t)(temp.to_ulong() >> 1*8);
		state[i*4+3] = (byte_t)(temp.to_ulong());
	}
}

4. 列混淆函数

/**
 * @description: 有限域2^8乘法运算
 * @param {byte_t} a:被乘数
 * @param {byte_t} b:乘数
 * @return {*}
 */
byte_t aes::GF2_8Mul(byte_t a, byte_t b)
{
	byte_t end(0x00);
	bool hit_1(0);
	for (int i = 0; i < 8; i++)
	{
		if (b[i] == 1)
		{
			end ^= a ;
		}
		hit_1 = (a[7] == 1); 
		a <<= 1;
		if (hit_1)
		{
			a ^= (byte_t)0x1b;
		}
	}
	return end;
}

/**
 * @description: 列混淆
 * @param {byte_t} state
 * @return {*}
 */
void aes::Mix_Columns(byte_t state[4*4])
{
	byte_t temp[4*4];
	for (int i = 0; i < 4; i++)
	{
		temp[i] = GF2_8Mul((byte_t)0x02,state[i])^GF2_8Mul((byte_t)0x03,state[i+4])^state[i+8]^state[i+12];
		temp[i+4] = state[i]^GF2_8Mul((byte_t)0x02,state[i+4])^GF2_8Mul((byte_t)0x03,state[i+8])^state[i+12];
		temp[i+8] = state[i]^state[i+4]^GF2_8Mul((byte_t)0x02,state[i+8])^GF2_8Mul((byte_t)0x03,state[i+12]);
		temp[i+12] = GF2_8Mul((byte_t)0x03,state[i])^state[i+4]^state[i+8]^GF2_8Mul((byte_t)0x02,state[i+12]);
	}
	for (int i = 0; i < 4*4; i++)
	{
		state[i] = temp[i];
	}
}

5. 轮密钥加

/**
 * @description: 4.轮密钥加
 * @param {byte_t} data
 * @param {word} key
 * @return {*}
 */
void aes::AddRoundKey(byte_t data[4*4],word key[4])
{
	for (int i = 0; i < 4; i++)
	{
		word key1 = (key[i] >> 24) & (word)(0x000000ff);//*移动到低八位
		word key2 = (key[i] >> 16) & (word)(0x000000ff);
		word key3 = (key[i] >> 8)  & (word)(0x000000ff);
		word key4 = key[i] & (word)(0x000000ff); 

		data[i]   ^= (byte_t)key1.to_ulong();
		data[i+4] ^= (byte_t)key2.to_ulong();
		data[i+8] ^= (byte_t)key3.to_ulong();
		data[i+12] ^= (byte_t)key4.to_ulong();
	}
	
}

6.加密解密函数

/**
 * @description: 加密
 * @param {byte_t data[4} *
 * @param {word exp_key[4} *
 * @return {*}
 */
string aes::encrypt(string strSrc, word* exp_key,unsigned char* cipher,int size_cipher)
{
	ZBase64 tool;
	//unsigned char data_temp[strlen(strSrc.c_str())+PLAINCODELENGTH];//*因为数组是静态空间,所以主动多申请一个正常明文的空间
	unsigned char data_temp[((strlen(strSrc.c_str())%16==0) ?(strlen(strSrc.c_str())+16) \
	: ((int)(strlen(strSrc.c_str())/16)*16)+16) ], data_reg[4*4];
	word init_key[4];
	int group(0),grouplen(0);
	byte_t data[4*4];

	PlainStringToHex(strSrc.c_str(),data_temp,grouplen);

	while(grouplen--){
		col_convert(&data_temp[group*16]);
		for (int i = 0; i < 16; i++)
		{
			data[i] = (byte_t)data_temp[i+(group*16)];
		}
		
		//*第一轮
		for (int i = 0; i < 4; i++)
		{
			init_key[i] = exp_key[i];
		} 
		AddRoundKey(data, init_key);
    	//* Nr轮加密
		for (int i = 1; i < Nr; i++)
		{
			SubBytes(data);
			ShiftRows(data);
			Mix_Columns(data);
			for (int j = 0; j < 4; j++)
			{
				init_key[j] = exp_key[i * 4 + j];
			}
			AddRoundKey(data, init_key);
		}
		//*最后一轮
		SubBytes(data);
		ShiftRows(data);
		for (int i = 0; i < 4; i++)
		{
			init_key[i] = exp_key[4 * Nr + i];
		}
		AddRoundKey(data, init_key);

		for(int i = 0; i < 16; i++)
		{
			data_reg[i] = (unsigned char)data[i].to_ulong();
		}
		col_convert(data_reg);
		for(int i = 0; i < 16; i++)
		{
			cipher[i+group*16] = data_reg[i];
		}
		group ++;//*移动到下一组
	}

	return tool.Encode(cipher,size_cipher);
}
/**
 * @description: decrypt one group 128bit data
 * @param {unsigned char} cipher_group
 * @param {word} exp_key
 * @param {unsigned char*} plain_group
 * @param {int} group
 * @return {*}
 */
void aes::group_decrypt(unsigned char cipher_group[4*4],word exp_key[4*(Nr+1)],unsigned char* plain_group,int group)
{
	byte_t data[4*4];
	word init_key[4];
	col_convert(cipher_group);

	for(int i = 0; i < 16; i++)
	{
		data[i] = (byte_t)cipher_group[i];
	}

	for (int i = 0; i < 4; i++)
	{
		init_key[i] = exp_key[4*Nr+i];
	}
	AddRoundKey(data, init_key);
	for (int i = Nr-1; i > 0; i--)
	{
		InvShiftRows(data);
		InvSubBytes(data);
		for (int j = 0; j < 4; j++)
		{
			init_key[j] = exp_key[i * 4 + j];
		}
		AddRoundKey(data, init_key);
		InvMixColumns(data);
	}
	InvShiftRows(data);
	InvSubBytes(data);
	for (int i = 0; i < 4; i++)
	{
		init_key[i] = exp_key[i];
	}
	
	AddRoundKey(data, init_key);

	byte_col_convert(data);


	for(int i = 0; i < 16; i++)
	{
		plain_group[i] = (unsigned char)data[i].to_ulong();
	}
}
/**
 * @description: decrypt
 * @param {string} strSrc:base64 cipher
 * @param {word exp_key[4} *
 * @param {unsigned char*} plaintext
 * @param {int&} plainLen
 * @return {*}plain string
 */
string aes::decrypt(string strSrc, word exp_key[4 * (Nr + 1)],unsigned char* plaintext,int& plainLen)
{
	ZBase64 tool;
	int cipher_len(0);
	unsigned char group_data[16];//*src is cipher(16*x)
	int group(0),group_len(0);
	string strPlain;
	string cipher_str = tool.Decode(strSrc.c_str(),strSrc.size(),cipher_len);
	
	memcpy(plaintext,cipher_str.c_str(),strlen(cipher_str.c_str()));

	group_len = cipher_len/16;

	while(group_len--){
		for(int i = 0; i < 16; i++)
		{
			group_data[i] = plaintext[i+(group*16)];
		}
		group_decrypt(group_data,exp_key,&plaintext[group*16],group);
		group ++;
	}

	strPlain=InvPlainPadding(plaintext,strlen(cipher_str.c_str()),plainLen);
	
	return strPlain;
}

7.类定义

这部分包含了所有子函数定义,包括未给出的在上面,具体实现见GitHub。

class aes
{
    public:
        aes();
        ~aes();

        word exp_key[4*(Nr+1)];//* 扩展密钥
        void KeyExpansion(string strKey,word exp_key[4*(Nr+1)]);//*密钥扩展
		string encrypt(string strSrc, word exp_key[4 * (Nr + 1)],unsigned char* cipher,int size_cipher);
		string decrypt(string strSrc, word exp_key[4 * (Nr + 1)],unsigned char* plaintext,int& plainLen);
    private:
		//*key expansion
        word byte2Word(byte_t& k1, byte_t& k2, byte_t& k3, byte_t& k4);
        void LeftShift(word& temp, int n);//*左移函数
        word SubWord(word& sw);
		void SubBytes(byte_t mtx[4*4]);
        word T(word temp, int round);
		//*1.字节代换
		//word SubWord(word& sw);
		//*2.行移位
		void ShiftRows(byte_t state[4*4]);
		//*3.列混淆
		byte_t GF2_8Mul(byte_t a, byte_t b);
		void Mix_Columns(byte_t state[4*4]);
		//*4.轮密钥加
		void AddRoundKey(byte_t data[4*4],word key[4]);

		void KeyStringToHex( const char* pSrc, unsigned char* pDest );
		void KeyPadding( unsigned char* pSrc, int nSrcLen );

		
		void PlainPadding( unsigned char* pSrc, int nSrcLen );
		string InvPlainPadding(unsigned char* pSrc,int srcLen,int& plainLen);//根据填充规则逆填充(比如4填充了四个数据本来是aaaa4444,后四个4是填充数据,经过逆填充变为aaaa)
		void PlainStringToHex(const char *pSrc, unsigned char *pDest,int& grouplen);//明文字符串转hex

		void col_convert(unsigned char mtx[4*4]);//*矩阵列转换
		void byte_col_convert(byte_t mtx[4*4]);
		void byte2char(byte_t* mtx,unsigned char* mtx_char,int mtx_len);//*byte转char

		//*逆s盒变换
		void InvSubBytes(byte_t mtx[4*4]);
		//*逆行变换
		void InvShiftRows(byte_t mtx[4*4]);
		//*逆列混淆
		void InvMixColumns(byte_t mtx[4*4]);
		//*128bit数据解密
		void group_decrypt(unsigned char cipher_group[4*4],word exp_key[4*(Nr+1)],unsigned char* plain_group,int group);
};

cbc模式

这个模式主要是引入了初始化向量,这个初始化向量与第一组明文异或再加密,然后第二组明文与第一组的密文异或后再加密。

  • 这样加密安全性很提高相比于ecb;
  • 因为每组明文都依赖与上一组的密文,所以是串行执行,速度降低。
    这部分的代码相比于ecb模式,只更改了加密解密函数以及引入了初始化向量函数。

更改/新加的函数


/**
 * @description: 加密
 * @param {byte_t data[4} *
 * @param {word exp_key[4} *
 * @return {*}
 */
string aes::encrypt(string strSrc, word* exp_key,unsigned char* cipher,int size_cipher)
{
	ZBase64 tool;
	//unsigned char data_temp[strlen(strSrc.c_str())+PLAINCODELENGTH];//*因为数组是静态空间,所以主动多申请一个正常明文的空间
	unsigned char data_temp[((strlen(strSrc.c_str())%16==0) ?(strlen(strSrc.c_str())+16) \
	: ((int)(strlen(strSrc.c_str())/16)*16)+16) ], data_reg[4*4];
	word init_key[4];
	int group(0),grouplen(0);
	byte_t data[4*4];

	PlainStringToHex(strSrc.c_str(),data_temp,grouplen);

	while(grouplen--){
		col_convert(&data_temp[group*16]);
		for (int i = 0; i < 16; i++)
		{
			data[i] = (byte_t)data_temp[i+(group*16)];
		}
		//*第一组明文与初始化向量异或,剩余与前一组密文异或
		col_convert(data_reg);
		if(group == 0){
			for (int i = 0; i < 16; i++){
				data[i] ^= (byte_t)cbc_iv[i];
			}
		}
		else{
			for (int i = 0; i < 16; i++){
				data[i] ^= (byte_t)data_reg[i];
			}
		}
		//*第一轮
		for (int i = 0; i < 4; i++)
		{
			init_key[i] = exp_key[i];
		} 
		AddRoundKey(data, init_key);
    	//* Nr轮加密
		for (int i = 1; i < Nr; i++)
		{
			SubBytes(data);
			ShiftRows(data);
			Mix_Columns(data);
			for (int j = 0; j < 4; j++)
			{
				init_key[j] = exp_key[i * 4 + j];
			}
			AddRoundKey(data, init_key);
		}
		//*最后一轮
		SubBytes(data);
		ShiftRows(data);
		for (int i = 0; i < 4; i++)
		{
			init_key[i] = exp_key[4 * Nr + i];
		}
		AddRoundKey(data, init_key);

		for(int i = 0; i < 16; i++)
		{
			data_reg[i] = (unsigned char)data[i].to_ulong();
		}
		col_convert(data_reg);
		for(int i = 0; i < 16; i++)
		{
			cipher[i+group*16] = data_reg[i];
		}
		group ++;//*移动到下一组
	}

	return tool.Encode(cipher,size_cipher);
}

/**
 * @description: decrypt
 * @param {string} strSrc:base64 cipher
 * @param {word exp_key[4} *
 * @param {unsigned char*} plaintext
 * @param {int&} plainLen
 * @return {*}plain string
 */
string aes::decrypt(string strSrc, word exp_key[4 * (Nr + 1)],unsigned char* plaintext,int& plainLen)
{
	ZBase64 tool;
	int cipher_len(0);
	unsigned char group_data[16],cipher_temp_last[16];//*src is cipher(16*x)
	int group(0),group_len(0);
	string strPlain;
	string cipher_str = tool.Decode(strSrc.c_str(),strSrc.size(),cipher_len);
	
	memcpy(plaintext,cipher_str.c_str(),strlen(cipher_str.c_str()));

	group_len = cipher_len/16;

	while(group_len--){
		memcpy(cipher_temp_last,group_data,16);
		col_convert(cipher_temp_last);
		for(int i = 0; i < 16; i++)
		{
			group_data[i] = plaintext[i+(group*16)];//*暂存密文数据
		}
		group_decrypt(group_data,exp_key,&plaintext[group*16],group);

		if(group == 0){
			for (int i = 0; i < 16; i++){
				plaintext[i+group*16] ^= cbc_iv[i];
			}
		}
		else{
			for (int i = 0; i < 16; i++){
				plaintext[i+group*16] ^= cipher_temp_last[i];
			}	
		}

		group ++;
	}

	strPlain=InvPlainPadding(plaintext,strlen(cipher_str.c_str()),plainLen);
	
	return strPlain;
}

void aes::initialization_vector(const char* iv)
{
	int nSrcLen = 0;
	if( iv != 0 )
	{
		nSrcLen = strlen(iv);
		memcpy(cbc_iv, iv, nSrcLen);
	}
	if( nSrcLen < 16 )
	{
		unsigned char ucPad = 16 - nSrcLen;//*填充的字节数
		for( int nID = 16; nID > nSrcLen; --nID )
		{
			cbc_iv[nID - 1] = ucPad;
		}
	}
}

完整代码地址

ecb模式
cbc模式

参考链接:AES加密算法的C++实现_aes加密 c+±CSDN博客

  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Max玮

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

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

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

打赏作者

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

抵扣说明:

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

余额充值