对于cpp来说内部有一些加密函数库来简单实现一些加密算法可以,但是为了更好理解内部实现流程,实现过程不调用cpp的库。
工程中出现的byte_t为bitset<8>类型,word为bitset<32>类型。base64转换文件为在网上找到的开源代码,在GitHub链接中有。
整体工程代码在末尾GitHub链接。
总体功能
- 实现了128/192/256三种密钥长度的ecb/cbc加密;
- 密钥输入可以少于实际要求输入,比如输入要求128bit但是少于128bit仍会填充;
- 实现了任意输入长度的明文输入(填充模式为pkcs7,可以大于小于128bit);
- 实现了base64的编码解码方便观察结果;
aes.h文件中有一些宏定义根据需要调整,明文长度只能固定为16,密钥长度24对应192bit模式。
ecb模式
1. 基本介绍
ecb模式是aes加密中最为简单的一个模式,该模式严格按照aes加密标准来执行加密(如下图)。
- 这个模式最大的优点是可以并行执行,因为前后明文密文互不干扰,所以这种加密方式是最快的执行的。
- 但是因为相同的明文会产生相同的密文,所以安全性比较差(效果如下图)。
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模式文章来源:https://www.toymoban.com/news/detail-756527.html
参考链接:AES加密算法的C++实现_aes加密 c+±CSDN博客文章来源地址https://www.toymoban.com/news/detail-756527.html
到了这里,关于AES算法基于FPGA的硬件实现(2)AES算法的c++实现(ecb/cbc)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!