AES(Advanced Encryption Standard)是一种对称加密算法,它是目前广泛使用的加密算法之一。AES算法是由美国国家标准与技术研究院(NIST)于2001年发布的,它取代了原先的DES(Data Encryption Standard)算法,成为新的标准。AES是一种对称加密算法,意味着加密和解密使用相同的密钥。这就要求密钥的安全性非常重要,因为任何拥有密钥的人都能进行加密和解密操作。其密钥长度,包括128位、192位和256位。不同长度的密钥提供了不同级别的安全性,通常更长的密钥长度意味着更高的安全性。
该算法支持多种工作模式,其中两种常见的模式是CBC(Cipher Block Chaining)和ECB(Electronic Codebook)。
在选择模式时,需要根据具体的应用场景和需求权衡安全性和性能。一般来说,CBC模式是更安全的选择,而ECB模式可能更容易实现和理解。在实际应用中,还可以考虑其他模式,如CTR(Counter)模式和GCM(Galois/Counter Mode)模式等,这些模式结合了安全性和性能的考虑。
本次案例中所需要使用的头文件信息如下所示;
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include
#include
#include
#include
#include
#include
extern "C"
{
#include
}
#pragma comment(lib,"libssl_static.lib")
#pragma comment(lib,"libcrypto.lib")
Cipher Block Chaining (CBC) 模式是一种对称加密的分组密码工作模式。在 CBC 模式中,明文被分成固定大小的块,并使用加密算法逐个处理这些块。每个块都与前一个块的密文进行异或运算,然后再进行加密。这个过程导致了一种“链接”效果,因此得名 Cipher Block Chaining。
以下是 CBC 模式的详细概述:
初始向量 (Initialization Vector, IV):
分组加密:
异或运算:
加密:
解密:
模式串行化:
填充:
安全性:
使用场景:
总体而言,CBC 模式提供了一种相对强大的加密方法,但在实现时需要注意使用随机且不可预测的 IV 以及处理填充的问题。
AES_set_encrypt_key
函数。具体来说,它用于将原始密钥设置为可以在 AES 加密算法中使用的格式。以下是该函数的原型:
int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
userKey
:指向用于设置密钥的输入数据的指针,即原始密钥。bits
:密钥长度,以比特为单位。在使用 AES 加密算法时,通常为 128、192 或 256。key
:指向 AES_KEY
结构的指针,用于存储设置后的密钥信息。该函数返回值为零表示成功,非零表示失败。成功调用后,key
参数中存储了经过格式化的密钥信息,可以在后续的 AES 加密操作中使用。
AES_cbc_encrypt
是 OpenSSL 库中用于执行 AES 算法中的 Cipher Block Chaining (CBC) 模式的函数。在 CBC 模式中,每个明文块在加密之前会与前一个密文块进行异或运算,以增加密码的随机性。
以下是 AES_cbc_encrypt
函数的原型:
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t length, const AES_KEY *key, unsigned char *ivec, const int enc);
in
:指向输入数据(明文)的指针。out
:指向输出数据(密文)的指针。length
:数据的长度,以字节为单位。key
:指向 AES_KEY
结构的指针,其中包含了加密密钥。ivec
:Initialization Vector(IV),用于增强密码的随机性,也是前一个密文块。在 CBC 模式中,IV 对于第一个数据块是必需的,之后的 IV 由前一个密文块决定。enc
:指定操作是加密(AES_ENCRYPT
)还是解密(AES_DECRYPT
)。AES_set_decrypt_key
函数。该函数用于将加密时使用的密钥调整为解密时使用的密钥,以便进行解密操作。
以下是 AES_set_decrypt_key
函数的原型:
int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
userKey
:指向用于设置解密密钥的输入密钥数据的指针。bits
:密钥长度,以比特为单位。支持的长度包括 128、192 和 256 比特。key
:指向 AES_KEY
结构的指针,该结构将存储设置后的解密密钥。实现加解密功能,如下openssl_aes_cbc_encrypt
用于使用CBC模式加密数据,openssl_aes_cbc_decrypt
则相反用于解密数据。
// 初始化密钥
const unsigned char key[AES_BLOCK_SIZE] = { 0x12,0x55,0x64,0x69,0xf1 };
// 初始化向量
unsigned char iv[AES_BLOCK_SIZE] = { 0 };
// AES CBC 模式加密
// 参数:
// - in: 待加密的数据
// - len: 待加密数据的长度
// - out: 存放加密结果的缓冲区
// 返回值:
// - 返回填充后加密数据的长度,失败返回-1
int openssl_aes_cbc_encrypt(char* in, size_t len, char* out)
{
AES_KEY aes;
// 填充数据为AES_BLOCK_SIZE的整数倍
char* aesIn;
int blockNum, aesInLen;
// 设置加密密钥
if (AES_set_encrypt_key(key, 128, &aes) < 0)
{
return -1;
}
// 判断原始数据长度是否AES_BLOCK_SIZE的整数倍
if ((len % AES_BLOCK_SIZE) != 0)
{
// 不是整数倍则用0填充
blockNum = len / AES_BLOCK_SIZE + 1;
aesInLen = blockNum * AES_BLOCK_SIZE;
aesIn = (char*)calloc(aesInLen, 1);
memcpy(aesIn, in, len);
}
else
{
aesInLen = len;
aesIn = (char*)calloc(aesInLen, 1);
memcpy(aesIn, in, len);
}
// AES CBC 模式加密
AES_cbc_encrypt((unsigned char*)aesIn, (unsigned char*)out, aesInLen, &aes, iv, AES_ENCRYPT);
// 释放分配的内存
free(aesIn);
// 返回填充后加密数据的长度
return aesInLen;
}
// AES CBC 模式解密
// 参数:
// - in: 待解密的数据
// - len: 待解密数据的长度
// - out: 存放解密结果的缓冲区
// 返回值:
// - 成功返回0,失败返回-1
int openssl_aes_cbc_decrypt(char* in, size_t len, char* out)
{
AES_KEY aes;
// 设置解密密钥
if (AES_set_decrypt_key(key, 128, &aes) < 0)
{
return -1;
}
// AES CBC 模式解密
AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
// 返回成功
return 0;
}
当需要对数据加密时,首先打开被加密文件这里我们打开的时csdn.zip
文件,加密后会写出为csdn.cbc
文件;
int main(int argc, char* argv[])
{
// 存放填充字节数的数组
char offset[4] = { '0' };
char* src = nullptr, *dst = nullptr;
int inlen, outlen, size;
FILE* srcFile, *dstFile;
// 打开被加密源文件
srcFile = fopen("d://comp/csdn.zip", "rb");
// 加密后写出文件
dstFile = fopen("d://comp/csdn.cbc", "wb+");
// 获取文件大小
fseek(srcFile, 0, SEEK_END);
inlen = ftell(srcFile);
if (inlen < 0)
{
return 0;
}
fseek(srcFile, 0, SEEK_SET);
// -------------------------------------------------------
// 开始加密
src = (char*)calloc(inlen, 1);
size = fread(src, 1, inlen, srcFile);
std::cout << "读入字节: " << size << std::endl;
// 输出变量申请的空间额外增加16字节
outlen = (inlen / 16 + 1) * 16;
dst = (char*)calloc(outlen, 1);
// 调用加密函数
size = openssl_aes_cbc_encrypt(src, inlen, dst);
// 获取填充的字节数,记录到输出文件的前4个字节内
sprintf(offset, "%d", size - inlen);
fwrite(offset, sizeof(char), 4, dstFile);
// -------------------------------------------------------
// 输出加密后的文件或者解密后的文件,文件大小应与原始文件一致
size = fwrite(dst, 1, size, dstFile);
std::cout << "输出文件大小: " << size << std::endl;
fcloseall();
free(src);
free(dst);
system("pause");
return 0;
}
解密时同样需要打开文件,将加密文件csdn.cbc
打开,并解密输出成csdnde.zip
文件;
int main(int argc, char* argv[])
{
// 存放填充字节数的数组
char offset[4] = { '0' };
char* src = nullptr, *dst = nullptr;
int inlen, outlen, size;
FILE* srcFile, *dstFile;
// 打开加密后的文件
srcFile = fopen("d://comp/csdn.cbc", "rb");
// 解密后写出的文件
dstFile = fopen("d://comp/csdnde.zip", "wb+");
// 获取文件大小
fseek(srcFile, 0, SEEK_END);
inlen = ftell(srcFile);
if (inlen < 0)
{
return 0;
}
fseek(srcFile, 0, SEEK_SET);
// -------------------------------------------------------
fread(offset, sizeof(char), 4, srcFile);
inlen -= 4;
src = (char*)calloc(inlen, 1);
// 从加密后的文件中获取填充的字节数
size = fread(src, 1, inlen, srcFile);
std::cout << "读入字节: " << size << std::endl;
// 得到原始文件的大小
size = size - atoi(offset);
outlen = (inlen / 16 + 1) * 16;
dst = (char*)calloc(outlen, 1);
// 解密
openssl_aes_cbc_decrypt(src, inlen, dst);
// -------------------------------------------------------
// 输出加密后的文件或者解密后的文件,文件大小应与原始文件一致
size = fwrite(dst, 1, size, dstFile);
std::cout << "输出文件大小: " << size << std::endl;
fcloseall();
free(src);
free(dst);
system("pause");
return 0;
}
运行后输出效果图如下所示;
Electronic Codebook (ECB) 模式是一种对称加密的分组密码工作模式。在 ECB 模式中,每个明文块都被独立加密,不受其他块的影响。这意味着相同的明文块将始终生成相同的密文块,这可能导致一些安全性问题。
以下是 ECB 模式的详细概述:
分组加密:
无链接:
模式串行化:
填充:
安全性问题:
使用场景:
总体而言,ECB 模式是一种简单的分组密码工作模式,但由于安全性问题,实际应用中更常使用其他工作模式。
AES_ecb_encrypt
是 OpenSSL 库中用于执行 AES 算法的 ECB 模式加密的函数。下面是对该函数的详细概述:
int AES_ecb_encrypt(const unsigned char *input, unsigned char *output, const AES_KEY *key, const int enc);
参数说明:
input
: 要加密的数据的输入缓冲区的指针。output
: 加密后的数据的输出缓冲区的指针。key
: AES 密钥的结构体指针,其中包含了加密所需的密钥信息。enc
: 一个整数值,用于指定是执行加密(AES_ENCRYPT
)还是解密(AES_DECRYPT
)操作。返回值:
功能说明:
AES_ecb_encrypt
函数用于在 ECB 模式下执行 AES 算法的加密或解密操作,具体取决于 enc
参数。key
参数提供的密钥信息执行加密或解密操作。AES_ecb_encrypt
是 OpenSSL 库中用于执行 AES 算法的 ECB 模式加密或解密的函数。下面是对该函数的详细概述:
int AES_ecb_encrypt(const unsigned char *input, unsigned char *output, const AES_KEY *key, const int enc);
参数说明:
input
: 要加密或解密的数据块的输入缓冲区指针。output
: 加密或解密后的数据块的输出缓冲区指针。key
: AES 密钥的结构体指针,包含了加密或解密所需的密钥信息。enc
: 一个整数值,用于指定是执行加密(AES_ENCRYPT
)还是解密(AES_DECRYPT
)操作。返回值:
功能说明:
AES_ecb_encrypt
函数用于在 ECB 模式下执行 AES 算法的加密或解密操作,具体取决于 enc
参数。key
参数提供的密钥信息执行加密或解密操作。// AES ECB 模式加密
// 参数:
// - in: 待加密的数据
// - len: 待加密数据的长度
// - out: 存放加密结果的缓冲区
// 返回值:
// - 成功返回填充后加密数据的长度,失败返回-1
int openssl_aes_ecb_enrypt(char* in, size_t len, char* out)
{
int i;
int blockNum;
int aesInLen;
char* aesIn;
AES_KEY aes;
// 设置加密密钥
if (AES_set_encrypt_key(key, 128, &aes) < 0)
return -1;
// 判断原始数据长度是否AES_BLOCK_SIZE的整数倍
if ((len % AES_BLOCK_SIZE) != 0)
{
blockNum = len / AES_BLOCK_SIZE + 1;
aesInLen = blockNum * AES_BLOCK_SIZE;
aesIn = (char*)calloc(aesInLen, 1);
memcpy(aesIn, in, len);
}
else
{
blockNum = len / AES_BLOCK_SIZE;
aesInLen = len;
aesIn = (char*)calloc(aesInLen, 1);
memcpy(aesIn, in, len);
}
// 由于ECB每次只处理AES_BLOCK_SIZE大小的数据,所以通过循环完成所有数据的加密
for (i = 0; i < blockNum; i++)
{
AES_ecb_encrypt((unsigned char*)aesIn, (unsigned char*)out, &aes, AES_ENCRYPT);
aesIn += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
// 释放内存
// free(aesIn);
// 返回填充后加密数据的长度
return aesInLen;
}
// AES ECB 模式解密
// 参数:
// - in: 待解密的数据
// - len: 待解密数据的长度
// - out: 存放解密结果的缓冲区
// 返回值:
// - 成功返回0,失败返回-1
int openssl_aes_ecb_decrypt(char* in, size_t len, char* out)
{
unsigned int i;
AES_KEY aes;
// 设置解密密钥
if (AES_set_decrypt_key(key, 128, &aes) < 0)
{
return -1;
}
// 循环解密每个数据块
for (i = 0; i < len / AES_BLOCK_SIZE; i++)
{
AES_ecb_encrypt((unsigned char*)in, (unsigned char*)out, &aes, AES_DECRYPT);
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
// 返回成功
return 0;
}
当需要对数据加密时,首先打开被加密文件这里我们打开的时csdn.zip
文件,加密后会写出为csdn.ecb
文件;
int main(int argc, char* argv[])
{
// 存放填充字节数的数组
char offset[4] = { '0' };
char* src = nullptr, *dst = nullptr;
int inlen, outlen, size;
FILE* srcFile, *dstFile;
// 打开被加密源文件
srcFile = fopen("d://comp/csdn.zip", "rb");
// 加密后写出文件
dstFile = fopen("d://comp/csdn.ecb", "wb+");
// 获取文件大小
fseek(srcFile, 0, SEEK_END);
inlen = ftell(srcFile);
if (inlen < 0)
{
return 0;
}
fseek(srcFile, 0, SEEK_SET);
// -------------------------------------------------------
// 开始加密
src = (char*)calloc(inlen, 1);
size = fread(src, 1, inlen, srcFile);
std::cout << "读入字节: " << size << std::endl;
// 输出变量申请的空间额外增加16字节
outlen = (inlen / 16 + 1) * 16;
dst = (char*)calloc(outlen, 1);
// ECB加密
size = openssl_aes_ecb_enrypt(src, inlen, dst);
sprintf(offset, "%d", size - inlen);
fwrite(offset, sizeof(char), 4, dstFile);
// -------------------------------------------------------
// 输出加密后的文件或者解密后的文件,文件大小应与原始文件一致
size = fwrite(dst, 1, size, dstFile);
std::cout << "输出文件大小: " << size << std::endl;
fcloseall();
free(src);
free(dst);
system("pause");
return 0;
}
运行后输出效果图如下所示;
解密时同样需要打开文件,将加密文件csdn.ecb
打开,并解密输出成csdnde.zip
文件;
int main(int argc, char* argv[])
{
// 存放填充字节数的数组
char offset[4] = { '0' };
char* src = nullptr, *dst = nullptr;
int inlen, outlen, size;
FILE* srcFile, *dstFile;
// 打开加密后的文件
srcFile = fopen("d://comp/csdn.ecb", "rb");
// 解密后写出的文件
dstFile = fopen("d://comp/csdnde.zip", "wb+");
// 获取文件大小
fseek(srcFile, 0, SEEK_END);
inlen = ftell(srcFile);
if (inlen < 0)
{
return 0;
}
fseek(srcFile, 0, SEEK_SET);
// -------------------------------------------------------
fread(offset, sizeof(char), 4, srcFile);
inlen -= 4;
src = (char*)calloc(inlen, 1);
// 从加密后的文件中获取填充的字节数
size = fread(src, 1, inlen, srcFile);
std::cout << "读入字节: " << size << std::endl;
// 得到原始文件的大小
size = size - atoi(offset);
outlen = (inlen / 16 + 1) * 16;
dst = (char*)calloc(outlen, 1);
// 解密
openssl_aes_ecb_decrypt(src, inlen, dst);
// -------------------------------------------------------
// 输出加密后的文件或者解密后的文件,文件大小应与原始文件一致
size = fwrite(dst, 1, size, dstFile);
std::cout << "输出文件大小: " << size << std::endl;
fcloseall();
free(src);
free(dst);
system("pause");
return 0;
}
运行后输出效果图如下所示;