CBC模式:Cipher Block Chaining mode(密码分组链接模式)
CBC模式的加解密
CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。密文分组像链条一样相互连接在一起。
CBC模式的加密流程图
CBC模式的解密流程图
将一个分组的加密过程分离出来,对ECB模式和CBC模式进行比较,ECB模式只进行了加密,而CBC模式则在加密之前进行了一次XOR。
ECB模式与CBC模式的比较
初始化向量
当加密第一个明文分组时,由于不存在”前一个密文分组“,因此需要事先准备一个长度为一个分组的比特序列来替代”前一个密文分组“,这个比特序列称为初始化向量,通常缩写为IV。通常,每次加密时都会随机产生一个不同的比特序列来作为初始化向量。
注:在UDS 27hex服务中不同的初始化向量可以代表着不同的解锁等级。
应用代码:演示加密和解密过程。
/**
* @file mainCBC.c
* @author jimu (minijimu@foxmail.com)
* @brief
* @version 0.1
* @date 2024-09-02
*
* @addtogroup 《图解密技术-第3版》
* @copyright Copyright (c) 2024
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes.h"
// 假设我们有一个密钥和一个初始化向量(IV)
uint8_t key[AES_KEYLEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};
uint8_t iv[AES_BLOCKLEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};
// 加密函数
void encrypt(uint8_t *plaintext, uint8_t *ciphertext, size_t length)
{
uint8_t *padded_text = (uint8_t *)malloc(length);
memset(padded_text, 0, length);
memcpy(padded_text, plaintext, length);
AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
if (length % AES_BLOCKLEN != 0)
{
printf("Error: Plaintext length must be a multiple of AES block size.\n");
exit(1);
}
AES_CBC_encrypt_buffer(&ctx, padded_text, length);
memcpy(ciphertext, padded_text, length);
free(padded_text);
}
// 解密函数
void decrypt(uint8_t *ciphertext, uint8_t *decryptedtext, size_t length)
{
AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
if (length % AES_BLOCKLEN != 0)
{
printf("Error: Ciphertext length must be a multiple of AES block size.\n");
exit(1);
}
AES_CBC_decrypt_buffer(&ctx, ciphertext, length);
memcpy(decryptedtext, ciphertext, length);
}
int main()
{
// 假设我们有一个明文消息
const uint8_t plaintext[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00};
size_t plaintext_len = sizeof(plaintext);
// 虽然plaintext_len 是 AES_BLOCKLEN 的倍数,但是在加密和解密之前,我们需要确保 plaintext_len 是 AES_BLOCKLEN 的倍数。
// 当前可认为是无填充模式。
if (plaintext_len % AES_BLOCKLEN != 0)
{
plaintext_len = (plaintext_len / AES_BLOCKLEN + 1) * AES_BLOCKLEN;
}
// 为密文和明文缓冲区分配内存
uint8_t *ciphertext = (uint8_t *)malloc(plaintext_len);
uint8_t *decryptedtext = (uint8_t *)malloc(plaintext_len);
printf("IV: ");
for (size_t i = 0; i < AES_BLOCKLEN; i++)
{
printf("%02x", iv[i]);
}
printf("\n");
printf("plaintext: ");
for (size_t i = 0; i < sizeof(plaintext); i++)
{
printf("%02x", plaintext[i]);
}
printf("\n");
// 加密
encrypt((uint8_t *)plaintext, ciphertext, plaintext_len);
// 打印密文
printf("Ciphertext text:");
for (size_t i = 0; i < plaintext_len; i++)
{
printf("%02x", ciphertext[i]);
}
printf("\n");
// 解密
decrypt(ciphertext, decryptedtext, plaintext_len);
// 打印解密后的明文
// printf("Decrypted text: %s\n", decryptedtext);
printf("Decrypted text: ");
for (size_t i = 0; i < plaintext_len; i++)
{
printf("%02x", decryptedtext[i]);
}
printf("\n");
// 使用 memcmp 比较原始明文和解密后的文本
if (memcmp(plaintext, decryptedtext, plaintext_len) == 0)
{
printf("Decryption successful!\n");
}
else
{
printf("Decryption failed!\n");
}
// 释放内存
free(ciphertext);
free(decryptedtext);
getchar();
return 0;
}
- 对比代码加密和工具加密的结果
代码简述
- 定义明文
plaintext
和其长度plaintext_len
。 - 确保
plaintext_len
是 AES 块大小的倍数。 - 分配内存用于存储密文和解密后的明文。
- 打印初始化向量(IV)和明文。
- 调用
encrypt
函数加密明文。 - 打印密文。
- 调用
decrypt
函数解密密文。 - 打印解密后的明文。
- 比较原始明文和解密后的明文,确认解密成功。
- 释放内存。
CBC模式应用场景
- 文件加密 :CBC 模式适合于加密静态文件,如备份文件或存储在硬盘上的数据。
- 通信加密 :在网络通信中,CBC 模式可用于加密数据包,尤其是在需要确保数据块间相关性的场景下。
- 数据库加密 :在数据库中存储敏感信息时,可以使用 CBC 模式来加密记录或字段。
实际应用注意事项
- IV 的管理 :IV 应该是随机的,并且对于每个加密操作都是唯一的。IV 需要与密文一起存储或传输,以便解密时使用。
- 填充机制 :选择合适的填充机制,并确保解密时正确去除填充。
- 完整性检查 :单独使用 CBC 模式无法提供数据完整性验证。通常需要结合 MAC(Message Authentication Code)或数字签名来确保数据的完整性和真实性。
并且对于每个加密操作都是唯一的。IV 需要与密文一起存储或传输,以便解密时使用。 - 填充机制 :选择合适的填充机制,并确保解密时正确去除填充。
- 完整性检查 :单独使用 CBC 模式无法提供数据完整性验证。通常需要结合 MAC(Message Authentication Code)或数字签名来确保数据的完整性和真实性。