分组密码的模式AES-ECB流程解析(应用代码实现)

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;
}
  • 下图发现:解密过后的文本和初始文本一致,通过在线计算工具得出加密后的数据和程序中的数据一致。

运行结果与在线计算结果对比

  • 代码总体介绍
    1. 通过对宏 STRING的值进行赋值可以选择使用字符串或者二进制数据作为明文。
    2. 密钥通过库函数生成的随机数获取。
    3. 初始化AES加密上下文,分块进行加解密,每次16个字节,如果最后一组数据小于16默认将后面的数据填充0。
    4. 原本想计算加解密的时间开销,可能是因为数据量太小,时钟精度又不够导致无法测量到运行的时间。
    5. 打印部分密钥、数据原文、密文、解密来的原文,并对比初始原文与解密来的原文是否一致来判断流程是否成功。
    6. block 数组是一个固定大小为 AES_BLOCKLEN为16字节的数组,用于存储一个完整的 AES 数据块。
    7. 该代码轻量化稍加修改可以移植到嵌入式系统中。
    8. 当前代码只考虑实际使用,并未考虑具体实现,有兴趣的伙伴可以参考openssl等开源安全算法。
  • 环境介绍
    • PC(Win10)
    • 编译器:VSCode
  • 版权说明:本文介绍部分参考了《图解密技术-第3版》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值