1、先贴出代码样例,再解读。代码中依赖openssl库使用AES的ECB(电码本)模式进行加密和解密。
2、电码本模式将明文分为若干个大小相同的块,对每个块进行单独的加密。不同的块加密结果没有关联,每个块可以独立解密。ECB模式的优点是简单,缺点是会被重放攻击。代码如下:
#include <openssl/aes.h>
// PKCS#7填充添加
void pkcs7_padding_add(unsigned char *in, int in_len, int block_size) {
int padding_len = block_size - (in_len % block_size);
for (int i = in_len; i < in_len + padding_len; i++) {
in[i] = (unsigned char)padding_len;
}
}
// PKCS#7填充删除
int pkcs7_padding_remove(unsigned char *in, int in_len, int block_size) {
int padding_len = in[in_len - 1];
if (padding_len < 1 || padding_len > block_size) {
return in_len;
}
for (int i = 1; i <= padding_len; i++) { // 检查填充位的值是否和填充长度相等,不等就返回实际长度。
if(in[in_len - i] != padding_len) {
return in_len;
}
}
return in_len - padding_len;
}
// 变长字符串,要带填充分块加密
int aes_ecb_pad_encode(const unsigned char* in, const unsigned int in_len, const unsigned char* aes_key, const unsigned int key_len, unsigned char* out, const unsigned int out_men_len)
{
if (in==NULL || in_len<=0 || aes_key==NULL || key_len<=0 || out==NULL || out_men_len<=0)
return -1;
// 计算填充后的大小
int block_size = AES_BLOCK_SIZE; // AES块的大小
int enc_slack_space = in_len % block_size;
int enc_spaces = in_len + (block_size - enc_slack_space);
if (enc_spaces > out_men_len)
{
return -2;
}
unsigned char enc_in[enc_spaces];
memset(enc_in, 0, sizeof(enc_in));
memcpy(enc_in, in, in_len);
pkcs7_padding_add(enc_in, in_len, block_size);
// 加密
AES_KEY enc_key;
AES_set_encrypt_key(aes_key, 128, &enc_key);
for (int i = 0; i < enc_spaces; i += block_size) {
AES_encrypt(enc_in + i, out + i, &enc_key);
}
return enc_spaces; //如果成功,返回密文长度
}
int aes_ecb_pad_decode(const unsigned char* in, const unsigned int in_len, const unsigned char* aes_key, const unsigned int key_len, unsigned char* out, const unsigned int out_men_len)
{
if (in==NULL || in_len<=0 || aes_key==NULL || key_len<=0 || out==NULL || out_men_len<=0)
return -1;
unsigned char dec_out[1024];
memset(dec_out, 0, sizeof(dec_out));
int enc_spaces = in_len;
int block_size = AES_BLOCK_SIZE;
if (enc_spaces%block_size != 0)
{ //密文不是block大小的整数倍
return -2;
}
if (enc_spaces-block_size>out_men_len)
{ //输出的明文内存太小
return -3;
}
AES_KEY dec_key;
AES_set_decrypt_key(aes_key, 128, &dec_key);
for (int i = 0; i < enc_spaces; i += block_size) {
AES_decrypt(in + i, out + i, &dec_key);
}
int dec_message_len = pkcs7_padding_remove(out, enc_spaces, block_size);
return dec_message_len; // 如果成功,返回明文长度
}
3、代码说明:
(1)加密函数 aes_ecb_pad_encode :
一个加密块的大小为16字节。调用 pkcs7_padding_add,这是填充函数。不管明文满不满足16字节的块大小,都需要进行填充。不是16的整数倍时,要填充补齐16字节。如果是16的整数倍,则需要填充一个整块。被填充的字节的值就是填充的长度。
AES_set_encrypt_key(aes_key, 128, &enc_key);是设置AES加密算法密钥的函数。
AES_encrypt(enc_in + i, out + i, &enc_key);这里进行加密。上述代码的调用可以看出该函数输入和输出是一个字节一个字节的处理的。enc_spaces是填充后的长度。
(2)解密函数 aes_ecb_pad_decode:
在函数AES_set_decrypt_key调用之前,都是参数检查,块长度计算等。
调用 AES_decrypt(in + i, out + i, &dec_key), 它也是一个字符一个字符的计算。
调用 pkcs7_padding_remove(out, enc_spaces, block_size),去除填充,便得到明文。