原文链接:
https://blog.csdn.net/mao834099514/article/details/54945776
密码学中的高级加密标准(Advanced Encryption Standard,AES),又称 Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
如下图所示,AES算法的数据分组长度为128比特、密钥长度为128/192/256比特。
2)CBC模式简介
下图中,IV一般为16字节全0,数据块长度为16字节的整数倍,则在此数据块后附加一个8字节长的数据块,
附加的数据块为:16进制的“80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00”
函数原型:
**int AES_set_encrypt_key(const unsigned char userKey, const int bits,AES_KEY key);
函数作用:
设定加密用的Key;
参数说明:
userKey: 密钥数值;
bits:密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128;
key: AES_KEY对象指针;
返回值: 0 成功, -1 userkey,key为空, -2: 密钥长度不是128,192,256;
函数原型:
**int AES_set_decrypt_key(const unsigned char userKey, const int bits, AES_KEY key);
函数作用:
设定解密用的Key;
参数说明:
userKey: 密钥数值;
bits:密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128;
key: AES_KEY对象指针;
返回值: 0 成功, -1 userkey,key为空, -2: 密钥长度不是128,192,256;
函数原型:
**void AES_ecb_encrypt(const unsigned char *in, unsigned char out, const AES_KEY key, const int enc);
函数说明:
AES加密/解密单个数据块(16个字节),ECB模式
参数说明:
in: 需要加密/解密的数据;
out: 计算后输出的数据;
key:密钥
enc: AES_ENCRYPT 代表加密, AES_DECRYPT代表解密;
函数原型:
**int AES_set_encrypt_key(const unsigned char userKey, const int bits,AES_KEY key);
函数作用:
设定加密用的Key;
参数说明:
userKey: 密钥数值;
bits:密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128;
key: AES_KEY对象指针;
返回值: 0 成功, -1 userkey,key为空, -2: 密钥长度不是128,192,256;
函数原型:
**int AES_set_decrypt_key(const unsigned char userKey, const int bits, AES_KEY key);
函数作用:
设定解密用的Key;
参数说明:
userKey: 密钥数值;
bits:密钥长度,以bit为单位,如果密钥数字是16个字节,则此参数值应为128;
key: AES_KEY对象指针;
返回值: 0 成功, -1 userkey,key为空, -2: 密钥长度不是128,192,256;
函数原型:
**void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY key, unsigned char ivec, const int enc);
函数作用:
AES加密/解密单个数据块(16个字节),CBC模式
参数说明:
in: 需要加密/解密的数据;
out: 计算后输出的数据;
length: 数据长度(这里不包含初始向量数据长度)
key:密钥
ivec: 初始向量(一般为16字节全0)
enc: AES_ENCRYPT 代表加密, AES_DECRYPT代表解密;
void CPage3::OnButtonEncrypt()
{
// TODO: Add your control notification handler code here
unsigned char key_hex[256] = {0};
unsigned char data_hex[256] = {0};
unsigned char initval_hex[256] = {0};
unsigned char temp[256] = {0};
int i = 0;
int keylen = 0;
int datalen = 0;
int InitialLen = 0;
AES_KEY key;
UpdateData(TRUE);
m_key.Remove(' ');
m_data.Remove(' ');
m_initval.Remove(' ');
keylen = m_key.GetLength()/2;
datalen = m_data.GetLength()/2;
InitialLen = m_initval.GetLength()/2;
if (datalen%16!=0)
{
AfxMessageBox("输入数据长度不是16的整数倍,请重新输入!");
return;
}
StrToHex(m_key,key_hex,keylen);
StrToHex(m_data,data_hex,datalen);
StrToHex(m_initval,initval_hex,InitialLen);
if (keylen == 16)
{
//设置加密密钥
AES_set_encrypt_key(key_hex,128,&key);
}
else if (keylen == 24)
{
//设置加密密钥
AES_set_encrypt_key(key_hex,192,&key);
}
else if (keylen == 32)
{
//设置加密密钥
AES_set_encrypt_key(key_hex,256,&key);
}
else
{
AfxMessageBox("输入密钥长度不是16/24/32字节,请重新输入!");
return;
}
//ECB模式
if (((CButton*)GetDlgItem(IDC_RADIO1))->GetCheck())
{
for(i = 0;i < datalen/16;i++)
{
AES_ecb_encrypt(data_hex+i*16, temp+i*16,&key,AES_ENCRYPT);
}
}
//CBC模式
else if (((CButton*)GetDlgItem(IDC_RADIO2))->GetCheck())
{
memcpy(data_hex+datalen,"\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",16);
datalen = datalen+16;
for(i = 0;i < datalen/16;i++)
{
AES_cbc_encrypt(data_hex+i*16, temp+i*16,16,&key,initval_hex, AES_ENCRYPT);
}
}
HexToStr(temp,datalen,m_result);
UpdateData(FALSE);
}
void CPage3::OnButtonDecrypt()
{
// TODO: Add your control notification handler code here
unsigned char key_hex[256] = {0};
unsigned char data_hex[256] = {0};
unsigned char initval_hex[256] = {0};
unsigned char temp[256] = {0};
int i = 0;
int keylen = 0;
int datalen = 0;
int InitialLen = 0;
AES_KEY key;
UpdateData(TRUE);
m_key.Remove(' ');
m_data.Remove(' ');
m_initval.Remove(' ');
keylen = m_key.GetLength()/2;
datalen = m_data.GetLength()/2;
InitialLen = m_initval.GetLength()/2;
if (datalen%16!=0)
{
AfxMessageBox("输入数据长度不是16的整数倍,请重新输入!");
return;
}
StrToHex(m_key,key_hex,keylen);
StrToHex(m_data,data_hex,datalen);
StrToHex(m_initval,initval_hex,InitialLen);
if (keylen == 16)
{
//设置解密密钥
AES_set_decrypt_key(key_hex,128,&key);
}
else if (keylen == 24)
{
//设置解密密钥
AES_set_decrypt_key(key_hex,192,&key);
}
else if (keylen == 32)
{
//设置解密密钥
AES_set_decrypt_key(key_hex,256,&key);
}
else
{
AfxMessageBox("输入密钥长度不是16/24/32字节,请重新输入!");
return;
}
//ECB模式
if (((CButton*)GetDlgItem(IDC_RADIO1))->GetCheck())
{
for(i = 0;i < datalen/16;i++)
{
AES_ecb_encrypt(data_hex+i*16, temp+i*16,&key,AES_DECRYPT);
}
}
//CBC模式
else if (((CButton*)GetDlgItem(IDC_RADIO2))->GetCheck())
{
for(i = 0;i < datalen/16;i++)
{
AES_cbc_encrypt(data_hex+i*16, temp+i*16,16,&key,initval_hex, AES_DECRYPT);
}
}
HexToStr(temp,datalen,m_result);
UpdateData(FALSE);
}
#include
void Widget::aesInit()
{
const char *key_string = "123456";
AES_KEY aes;
int i = 0;
unsigned char out1[16];
unsigned char out2[16];
memset(out1,0,16);
memset(out2,0,16);
if (AES_set_encrypt_key((unsigned char*)key_string, 128, &aes) < 0) {
qDebug()<<"Unable to set encryption key in AES\n";
exit(-1);
}
char temp[16] = "ABCDEDDFDSGFRSF";
AES_encrypt((unsigned char*)temp,out1,&aes);
for(i= 0;i < 16;i++){
qDebug("%X",out1[i]);
}
if (AES_set_decrypt_key((unsigned char*)key_string, 128, &aes) < 0) {
qDebug()<<"Unable to set encryption key in AES\n";
exit(-1);
}
AES_decrypt(out1,out2,&aes);
qDebug()<<"temp=\n"<<temp<<"out2=\n"<<out2;
}
注意事项:
1. 调用加密函数之前,必须先设置加密key:AES_set_encrypt_key
2. 调用解密函数之前,必须先设置解密key:AES_set_decrypt_key
3. 对于AES_cbc_模式加密解密,每次调用前必须先初始化ivec向量
" crypto/aes/ aes_ecb.c"
void AES_ecb_encrypt ( const unsigned char * in , unsigned char * out ,
const AES_KEY * key , const int enc ) {
assert ( in && out && key );
assert (( AES_ENCRYPT == enc )||( AES_DECRYPT == enc ));
if ( AES_ENCRYPT == enc )
AES_encrypt ( in , out , key );
else
AES_decrypt ( in , out , key );
}
从这里可以看出,ecb方式的加密,是由AES_encrypt接口实现的。
AES_encrypt 就是ecb加密的方式
AES_encrypt/AES_decrypt一次只处理16个字节。如果输入数据较长,你需要使用循环语句,每16个字节处理一次,直到所有数据处理完毕。如果数据不足16字节,可以用0填充至16字节。
这个函数比AES_encrypt多了一个ivec参数,ivec的内容可以任意指定,但是加密和解密操作必须使用同样的数据。在AES_cbc_encrypt底层,实际上是每16个字节做一次处理,先和ivec做异或运算,然后调用AES_encrypt函数进行加密。
AES_cbc_encrypt在加密的过程中会修改ivec的内容,因此ivec参数不能是一个常量,而且不能在传递给加密函数后再立马传递给解密函数,必须重新赋值之后再传递给解密函数。
关于输出数据的长度
输出数据缓冲区的长度必须是16字节的倍数,加密完成后,比输入长度多出来的输出数据是不可以丢弃的。因此,存档的时候,需要记录原始数据的长度。
原文链接:
https://blog.csdn.net/zyhse/article/details/112291862?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_utm_term~default-0.no_search_link&spm=1001.2101.3001.4242.1
加密反馈模式 Cipher Feedback Mode(CFB)。面向字符的应用程序的加密要使用流加密法,可以使用加密反馈模式。在此模式下, 数据用更小的单元加密,如可以是8 位,这个长度小于定义的块长(通常是64 位)。
AES CFB128加密/解密:
void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,
size_t length, const AES_KEY *key,
unsigned char *ivec, int *num, const int enc);
参数名称 | 含义 |
---|---|
in | 输入数据,长度任意 |
out | 输出数据,长度与输入数据相等 |
length | 输入数据的长度,字节数 |
key | 使用AES_set_encrypt_key生成的Key |
ivec | 可读写的一块内存。长度必须是16字节。 |
num | 应总是为0,否则会触发断言 |
enc | AES_ENCRYPT 代表加密, AES_DECRYPT代表解密 |
AES_cfb128_encrypt在加密的过程中会修改ivec的内容,因此ivec参数不能是一个常量,而且不能在传递给加密函数后再立马传递给解密函数,必须重新赋值之后再传递给解密函数。
(1)特别注意
CFB模式加密和解密均使用加密key,这一点比较反常,务必记住。
CFB模式不需要对输入数据进行填充。
AES_cfb128_encrypt函数length参数,为输入数据长度,字节数。这一点与CFB1模式中有所不同。
(2)实现CFB128模式加解密
下面,函数已经封装完毕,如下:
/**
* @brief AES::cfb128_encrypt
* CFB128模式加解密,支持对任意长度明文进行加解密。
* @param in 输入数据
* @param out 输出结果
* @param key 密钥,长度必须是16/24/32字节,否则加密失败
* @param ivec 初始向量,长度必须是16字节
* @param enc true-加密,false-解密
* @return 执行结果
*/
bool AES::cfb128_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
// 检查密钥合法性(只能是16、24、32字节)
Q_ASSERT(key.size() == 16 || key.size() == 24 || key.size() == 32);
Q_ASSERT(ivec.size() == 16); // 初始向量为16字节
// 特别注意:CFB模式加密和解密均使用加密key。
// 生成加密key
AES_KEY aes_key;
if (AES_set_encrypt_key((const unsigned char*)key.data(), key.size() * 8, &aes_key) != 0)
{
return false;
}
// 执行CFB128模式加密或解密
int num = 0;
QByteArray ivecTemp = ivec; // ivec会被修改,故需要临时变量来暂存
int encVal = enc ? AES_ENCRYPT : AES_DECRYPT;
out.resize(in.size()); // 调整输出buf大小
AES_cfb128_encrypt((const unsigned char*)in.data(),
(unsigned char*)out.data(),
in.size(),
&aes_key,
(unsigned char*)ivecTemp.data(),
&num,
encVal);
return true;
}
加密过程:
生成加密key
执行加密
解密过程:
生成加密key
执行解密
经测试,本函数支持对任意长度输入数据进行加解密。
void createTestData(QByteArray& data, int size)
{
data.resize(size);
for (int i = 0; i < size; i++)
{
data[i] = i % 128;
}
}
void testAES(const QByteArray& data)
{
QByteArray plainText = data;
QByteArray encryptText;
QByteArray decryptText;
QByteArray key = QByteArray::fromHex("8cc72b05705d5c46f412af8cbed55aad");
QByteArray ivec = QByteArray::fromHex("667b02a85c61c786def4521b060265e8");
// AES cfb128模式加密验证
AES aes;
aes.cfb128_encrypt(plainText, encryptText, key, ivec, true); // 加密
aes.cfb128_encrypt(encryptText, decryptText, key, ivec, false); // 解密
qDebug() << "AES cfb128 encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 产生1MB+3B的测试数据,为了使该测试数据长度,不为8或16的整数倍
QByteArray data;
createTestData(data, 1*1024*1024+3);
// 测试AES
testAES(data); // 测试,直接调用OpenSSL中AES算法函数
return a.exec();
}
实战篇-OpenSSL之调用EVP框架实现AES多种加密模式