ECB模式:Electronic CodeBook mode(电子密码本模式)
术语 | 说明 |
---|---|
明文分组 | 分组密码算法中作为加密对象的明文。 |
密文分组 | 分组密码算法将明文分组加密后生成的密文。 |
ECB模式介绍
ECB模式全程Electronic CodeBook模式。在ECB模式中,将明文分组加密之后的结果直接成为密文分组。
使用ECB模式加密时,相同的明文分组会被转换为相同的密文分组,可以理解为“明文分组->密文分组”的对应表,因此ECB模式也称为**电子密码本模式**。
当最后一个明文分组的内容小于分组长度时,需要用一些特定的数据进行**填充**。
ECB模式的特点
ECB模式中,明文分组与密文分组是一一对应的关系,因此,如果明文中存在多个相同的分组,则这些明文分组最终将被转化成相同的密码分组。ECB模式也算所有模式中最简单的一种。
应用代码实现
/**
* @file mainECB.c
* @author jimu (minijimu@foxmail.com)
* @brief AES ECB 加密解密示例程序
* @version 0.1
* @date 2024-08-31
*
* @copyright Copyright (c) 2024
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "aes.h"
#define AES_STRING 0
// 生成随机密钥的函数
uint8_t *generate_random_key(size_t key_length)
{
uint8_t *key = (uint8_t *)malloc(key_length);
if (!key)
{
printf("Error: Failed to allocate memory for key.\n");
return NULL;
}
// 初始化随机数生成器
srand(time(NULL));
// 生成随机密钥
for (size_t i = 0; i < key_length; i++)
{
key[i] = rand() % 256; // 生成 0 到 255 之间的随机数
}
return key;
}
int main()
{
// 原始数据
#if AES_STRING
const char *plaintext = "Copyright 2024 Jimu!";
size_t plaintext_len = strlen(plaintext);
#else
const uint8_t plaintext[] = {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10};
size_t plaintext_len = sizeof(plaintext);
#endif
// 生成随机密钥
uint8_t *key = generate_random_key(AES_KEYLEN);
if (!key)
{
return 1;
}
printf("Key: ");
for (size_t i = 0; i < AES_KEYLEN; i++)
{
printf("%02x", key[i]);
}
printf("\n");
// 初始化上下文
struct AES_ctx ctx;
AES_init_ctx(&ctx, key);
// 加密
uint8_t *ciphertext = (uint8_t *)malloc(plaintext_len);
if (!ciphertext)
{
printf("Error: Failed to allocate memory for ciphertext.\n");
free(key);
return 1;
}
memset(ciphertext, 0, plaintext_len);
struct timeval start, end;
double elapsed;
// 计时开始
gettimeofday(&start, NULL);
// 分块加密
for (size_t i = 0; i < plaintext_len; i += AES_BLOCKLEN)
{
uint8_t block[AES_BLOCKLEN] = {0};
memcpy(block, &plaintext[i], AES_BLOCKLEN);
AES_ECB_encrypt(&ctx, block);
memcpy(&ciphertext[i], block, AES_BLOCKLEN);
}
// 计时结束
gettimeofday(&end, NULL);
elapsed = (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) / 1000000.0;
printf("Encryption time: %.6f seconds\n", elapsed);
// 解密
uint8_t *decryptedtext = (uint8_t *)malloc(plaintext_len);
if (!decryptedtext)
{
printf("Error: Failed to allocate memory for decryptedtext.\n");
free(ciphertext);
free(key);
return 1;
}
memset(decryptedtext, 0, plaintext_len);
// 计时开始
gettimeofday(&start, NULL);
// 分块解密
for (size_t i = 0; i < plaintext_len; i += AES_BLOCKLEN)
{
uint8_t block[AES_BLOCKLEN];
memcpy(block, &ciphertext[i], AES_BLOCKLEN);
AES_ECB_decrypt(&ctx, block);
memcpy(&decryptedtext[i], block, AES_BLOCKLEN);
}
// 计时结束
gettimeofday(&end, NULL);
elapsed = (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) / 1000000.0;
printf("Decryption time: %.6f seconds\n", elapsed);
// 打印结果
#if AES_STRING
printf("Original: %s\n", plaintext);
printf("Encrypted: ");
for (size_t i = 0; i < plaintext_len; i++)
{
printf("%02x", ciphertext[i]);
}
printf("\nDecrypted: %s\n", decryptedtext);
#else
printf("Original: ");
for (size_t i = 0; i < plaintext_len; i++)
{
printf("%02x", plaintext[i]);
}
printf("\nEncrypted: ");
for (size_t i = 0; i < plaintext_len; i++)
{
printf("%02x", ciphertext[i]);
}
printf("\nDecrypted: ");
for (size_t i = 0; i < plaintext_len; i++)
{
printf("%02x", decryptedtext[i]);
}
#endif
if (memcmp(plaintext, decryptedtext, plaintext_len) != 0)
{
printf("\nDecryption failed!");
}
else
{
printf("\nDecryption successful!");
}
printf("\n");
// 释放内存
free(ciphertext);
free(decryptedtext);
free(key);
getchar();
return 0;
}
- 下图发现:解密过后的文本和初始文本一致,通过在线计算工具得出加密后的数据和程序中的数据一致。
- 代码总体介绍
- 通过对宏
STRING
的值进行赋值可以选择使用字符串或者二进制数据作为明文。 - 密钥通过库函数生成的随机数获取。
- 初始化AES加密上下文,分块进行加解密,每次16个字节,如果最后一组数据小于16默认将后面的数据填充0。
- 原本想计算加解密的时间开销,可能是因为数据量太小,时钟精度又不够导致无法测量到运行的时间。
- 打印部分密钥、数据原文、密文、解密来的原文,并对比初始原文与解密来的原文是否一致来判断流程是否成功。
block
数组是一个固定大小为AES_BLOCKLEN
为16字节的数组,用于存储一个完整的 AES 数据块。- 该代码轻量化稍加修改可以移植到嵌入式系统中。
- 当前代码只考虑实际使用,并未考虑具体实现,有兴趣的伙伴可以参考openssl等开源安全算法。
- 通过对宏
- 环境介绍
- PC(Win10)
- 编译器:VSCode
- 版权说明:本文介绍部分参考了《图解密技术-第3版》