openssl编程--AES_cbc_encrypt()

本文介绍了AES加密中CBC模式的实现原理及应用。通过具体的C语言代码示例,详细解释了CBC模式下加密和解密的过程,包括初始化向量的使用、密钥设置等关键步骤,并展示了实际运行结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自: http://yuanshuilee.blog.163.com/blog/static/21769727520140942826137/
稍作注释。
AES有几种扩展算法,其中ecb和cbc需要填充,即加密后长度可能会不一样,cfb和ofb不需要填充,密文长度与明文长度一样
1. 对称加密(比如AES)加密后的结果都是一个某个数(比如128bit=16字节)的整倍数。
2. 对称加密(比如AES)大部分都是分块加密的。比如你加密1个字节和加密2个字节的结果长多都是一样的。
因为都没有超过加密最小块的大小,如果不足,会自动填充的。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/aes.h>
#define AES_BITS 128
#define MSG_LEN 128

int aes_encrypt(char* in, char* key, char* out,int len)//, int olen)可能会设置buf长度
{
    if(!in || !key || !out) return 0;
    unsigned char iv[AES_BLOCK_SIZE];//加密的初始化向量
    for(int i=0; i<AES_BLOCK_SIZE; ++i)//iv一般设置为全0,可以设置其他,但是加密解密要一样就行
        iv[i]=0;
    AES_KEY aes;
    if(AES_set_encrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }
    //int len=strlen(in);//这里的长度是char*in的长度,但是如果in中间包含'\0'字符的话

    //那么就只会加密前面'\0'前面的一段,所以,这个len可以作为参数传进来,记录in的长度

    //至于解密也是一个道理,光以'\0'来判断字符串长度,确有不妥,后面都是一个道理。
    AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_ENCRYPT);
    return 1;
}
int aes_decrypt(char* in, char* key, char* out,int len)
{
    if(!in || !key || !out) return 0;
    unsigned char iv[AES_BLOCK_SIZE];//加密的初始化向量
    for(int i=0; i<AES_BLOCK_SIZE; ++i)//iv一般设置为全0,可以设置其他,但是加密解密要一样就行
        iv[i]=0;
    AES_KEY aes;
    if(AES_set_decrypt_key((unsigned char*)key, 128, &aes) < 0)
    {
        return 0;
    }
 //   int len=strlen(in);
    AES_cbc_encrypt((unsigned char*)in, (unsigned char*)out, len, &aes, iv, AES_DECRYPT);
    return 1;
}

int main(int argc,char *argv[])
{
    char sourceStringTemp[MSG_LEN];
    char dstStringTemp[MSG_LEN];
    memset((char*)sourceStringTemp, 0 ,MSG_LEN);
    memset((char*)dstStringTemp, 0 ,MSG_LEN);
    strcpy((char*)sourceStringTemp, "123456789 123456789 123456789 12a");
    //strcpy((char*)sourceStringTemp, argv[1]);

    char key[AES_BLOCK_SIZE];
    int i;
    for(i = 0; i < 16; i++)//可自由设置密钥
    {
        key[i] = 32 + i;
    }
    int sour_len = strlen((char*)sourceStringTemp);
    if(!aes_encrypt(sourceStringTemp,key,dstStringTemp,MSG_LEN))
    {
        printf("encrypt error\n");
        return -1;
    }
    printf("enc %d:",strlen((char*)dstStringTemp));
    for(i= 0;dstStringTemp[i];i+=1){
        printf("%x",(unsigned char)dstStringTemp[i]);
    }
    memset((char*)sourceStringTemp, 0 ,MSG_LEN);
    if(!aes_decrypt(dstStringTemp,key,sourceStringTemp,MSG_LEN))
    {
        printf("decrypt error\n");
        return -1;
    }
    printf("\n");
    printf("dec %d:",strlen((char*)sourceStringTemp));
    printf("%s\n",sourceStringTemp);
    for(i= 0;i < sour_len;i+=1){
        printf("%x",(unsigned char)sourceStringTemp[i]);
    }
    printf("\n");
    return 0;
}
在以下函数的基础上,改写为 使用 CTR/OFB/CFB 加密方式。提供修改后的完整代码 #include "aes_crypto.h" #include <openssl/err.h> #include <string.h> #include <stdio.h> int aes_init() { // 初始化OpenSSL库 if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL)) { return AES_ERR_INIT_FAILED; } return AES_SUCCESS; } void aes_cleanup() { // 清理OpenSSL资源 EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); } int aes_generate_key(int key_len, AES_KEY *key) { if (!key || (key_len != 128 && key_len != 192 && key_len != 256)) { return AES_ERR_INVALID_INPUT; } // 生成随机密钥 if (RAND_bytes(key->key, key_len / 8) != 1) { return AES_ERR_KEY_GEN_FAILED; } // 生成随机初始化向量 if (RAND_bytes(key->iv, sizeof(key->iv)) != 1) { return AES_ERR_KEY_GEN_FAILED; } key->key_len = key_len; return AES_SUCCESS; } int aes_save_key(const AES_KEY *key, const char *filename) { if (!key || !filename) { return AES_ERR_INVALID_INPUT; } FILE *fp = fopen(filename, "wb"); if (!fp) { return AES_ERR_KEY_SAVE_FAILED; } // 写入密钥长度 if (fwrite(&key->key_len, sizeof(int), 1, fp) != 1) { fclose(fp); return AES_ERR_KEY_SAVE_FAILED; } // 写入密钥 int key_bytes = key->key_len / 8; if (fwrite(key->key, 1, key_bytes, fp) != key_bytes) { fclose(fp); return AES_ERR_KEY_SAVE_FAILED; } // 写入IV if (fwrite(key->iv, 1, sizeof(key->iv), fp) != sizeof(key->iv)) { fclose(fp); return AES_ERR_KEY_SAVE_FAILED; } fclose(fp); return AES_SUCCESS; } int aes_load_key(AES_KEY *key, const char *filename) { if (!key || !filename) { return AES_ERR_INVALID_INPUT; } FILE *fp = fopen(filename, "rb"); if (!fp) { return AES_ERR_KEY_LOAD_FAILED; } // 读取密钥长度 if (fread(&key->key_len, sizeof(int), 1, fp) != 1) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } // 验证密钥长度有效性 if (key->key_len != 128 && key->key_len != 192 && key->key_len != 256) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } // 读取密钥 int key_bytes = key->key_len / 8; if (fread(key->key, 1, key_bytes, fp) != key_bytes) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } // 读取IV if (fread(key->iv, 1, sizeof(key->iv), fp) != sizeof(key->iv)) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } fclose(fp); return AES_SUCCESS; } int aes_encrypt(const AES_KEY *key, const unsigned char *plaintext, int pt_len, unsigned char *ciphertext) { if (!key || !plaintext || !ciphertext || pt_len <= 0) { return -AES_ERR_INVALID_INPUT; } EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { return -AES_ERR_ENCRYPT_FAILED; } // 根据密钥长度选择算法 const EVP_CIPHER *cipher = NULL; switch (key->key_len) { case 128: cipher = EVP_aes_128_cbc(); break; case 192: cipher = EVP_aes_192_cbc(); break; case 256: cipher = EVP_aes_256_cbc(); break; default: EVP_CIPHER_CTX_free(ctx); return -AES_ERR_INVALID_INPUT; } int len; int ciphertext_len; // 初始化加密操作 if (EVP_EncryptInit_ex(ctx, cipher, NULL, key->key, key->iv) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_ENCRYPT_FAILED; } // 执行加密 if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, pt_len) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_ENCRYPT_FAILED; } ciphertext_len = len; // 完成加密 if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_ENCRYPT_FAILED; } ciphertext_len += len; EVP_CIPHER_CTX_free(ctx); return ciphertext_len; } int aes_decrypt(const AES_KEY *key, const unsigned char *ciphertext, int ct_len, unsigned char *plaintext) { if (!key || !ciphertext || !plaintext || ct_len <= 0) { return -AES_ERR_INVALID_INPUT; } EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { return -AES_ERR_DECRYPT_FAILED; } // 根据密钥长度选择算法 const EVP_CIPHER *cipher = NULL; switch (key->key_len) { case 128: cipher = EVP_aes_128_cbc(); break; case 192: cipher = EVP_aes_192_cbc(); break; case 256: cipher = EVP_aes_256_cbc(); break; default: EVP_CIPHER_CTX_free(ctx); return -AES_ERR_INVALID_INPUT; } int len; int plaintext_len; // 初始化解密操作 if (EVP_DecryptInit_ex(ctx, cipher, NULL, key->key, key->iv) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_DECRYPT_FAILED; } // 执行解密 if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ct_len) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_DECRYPT_FAILED; } plaintext_len = len; // 完成解密 if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_DECRYPT_FAILED; } plaintext_len += len; EVP_CIPHER_CTX_free(ctx); return plaintext_len; }
最新发布
08-14
<think>我们注意到原代码中使用了CBC模式,现在要改为支持CTR、OFB、CFB模式。 修改点: 1. 在加密和解密函数中,根据不同的模式选择不同的EVP_CIPHER。 2. 注意:CTR、OFB、CFB是流加密模式,不需要填充。因此,我们需要禁用填充(通过EVP_CIPHER_CTX_set_padding)。 3. 但是,原代码中并没有显式设置填充,因为默认是开启填充的(对于CBC模式是必要的)。对于流模式,我们必须禁用填充。 然而,我们如何让函数支持多种模式?我们可以修改AES_KEY结构体,增加一个字段表示模式,或者在函数参数中增加一个模式参数。 但是,用户要求是修改函数以支持这些模式,而原函数的接口是固定的。因此,我们需要调整设计。 考虑到原代码中AES_KEY结构体定义在aes_crypto.h中,我们可以修改这个头文件,在AES_KEY结构体中增加一个字段,比如: int mode; // 表示加密模式,例如我们可以定义常量:AES_MODE_CBC=0, AES_MODE_CTR=1, AES_MODE_OFB=2, AES_MODE_CFB=3 但是,题目没有给出aes_crypto.h的内容,所以我们需要假设可以修改它。如果无法修改,可能需要通过其他方式(比如全局变量)来设置模式,但这不推荐。 另一种方法:在aes_encryptaes_decrypt函数中增加一个参数来指定模式。但是,这样会改变函数签名,可能不符合要求。 由于题目要求是“改写”函数,并且没有说明不能修改结构体,我们选择修改AES_KEY结构体,增加一个mode字段。 步骤: 1. 在aes_crypto.h中的AES_KEY结构体增加一个int mode; 并且定义几个常量表示模式。 2. 在aes_generate_key函数中,默认设置为什么模式?原代码没有,所以我们需要在生成密钥时指定模式?或者可以在保存密钥时保存模式?或者在使用时设置? 3. 为了保持一致性,我们可以在aes_generate_key中增加一个参数来指定模式,但这样会改变函数签名。或者我们可以在生成密钥后单独设置模式。 考虑到题目要求,我们可能不想改变其他函数的签名。因此,我们可以在AES_KEY结构体中存储模式,并在aes_generate_key中设置一个默认模式(比如CBC,以保持向后兼容),但这样不够灵活。 另一种做法:修改aes_generate_key,增加一个参数来指定模式。同样,aes_load_key和aes_save_key也需要能够保存和加载这个模式。 由于题目要求是修改加密函数以支持这些模式,我们这里假设可以修改结构体和相关函数。 具体修改如下: 修改aes_crypto.h: #define AES_MODE_CBC 0 #define AES_MODE_CTR 1 #define AES_MODE_OFB 2 #define AES_MODE_CFB 3 typedef struct { int key_len; unsigned char key[32]; // 256 bits, 32 bytes unsigned char iv[16]; // 128 bits, 16 bytes int mode; // 新增字段,表示加密模式 } AES_KEY; 然后,修改aes_generate_key函数,增加一个参数来指定模式: int aes_generate_key(int key_len, int mode, AES_KEY *key); // 增加mode参数 但是,这样会改变函数签名,可能影响其他代码。如果不想改变签名,我们可以让模式在生成密钥后单独设置,比如增加一个函数: void aes_set_mode(AES_KEY *key, int mode); 但是,题目没有要求,为了简单起见,我们修改aes_generate_key的签名,并同时修改aes_save_key和aes_load_key以保存和读取模式。 由于修改函数签名影响较大,我们也可以选择在生成密钥时默认使用CBC模式(这样不改变签名),然后通过单独的函数设置模式。这里为了演示,我们选择修改aes_generate_key的签名。 同时,我们也要修改aes_save_key和aes_load_key,在文件中保存模式。 修改aes_save_key: // 写入密钥长度 // 写入密钥 // 写入IV // 写入模式 if (fwrite(&key->mode, sizeof(int), 1, fp) != 1) { // 错误处理 } 同理,aes_load_key也要读取模式。 然后,在aes_encryptaes_decrypt中,根据key->mode来选择不同的EVP_CIPHER,并设置禁用填充(对于CTR、OFB、CFB模式)。 注意:CBC模式仍然需要填充,所以我们只对非CBC模式禁用填充。 具体修改aes_encryptaes_decrypt: 在加密函数中: const EVP_CIPHER *cipher = NULL; switch (key->key_len) { case 128: switch (key->mode) { case AES_MODE_CBC: cipher = EVP_aes_128_cbc(); break; case AES_MODE_CTR: cipher = EVP_aes_128_ctr(); break; case AES_MODE_OFB: cipher = EVP_aes_128_ofb(); break; case AES_MODE_CFB: cipher = EVP_aes_128_cfb128(); break; // 注意:CFB有不同版本,这里使用128位的CFB,即CFB128 default: // 错误 } break; case 192: // 类似,使用EVP_aes_192_cbc, EVP_aes_192_ctr等 break; case 256: // 类似 break; default: // 错误 } 然后,在初始化加密上下文后,如果不是CBC模式,则禁用填充: if (key->mode != AES_MODE_CBC) { EVP_CIPHER_CTX_set_padding(ctx, 0); } 同样,在解密函数中做同样的修改。 注意:CFB模式有多个版本,如CFB1、CFB8、CFB128。这里我们使用CFB128,即每次处理128位(一个块)。如果需要其他版本,可以再扩展,但题目没有要求,我们就用CFB128。 另外,由于流模式加密后的密文长度等于明文长度,所以不需要额外的填充空间。但是原代码中调用者传入的ciphertext缓冲区应该足够大(原代码没有检查缓冲区大小,我们假设调用者已经分配了足够的空间,对于CBC模式,需要至少pt_len + AES_BLOCK_SIZE,而对于流模式,只需要pt_len)。因此,使用不同模式时,调用者需要根据模式调整缓冲区大小。但原函数接口没有改变,所以调用者可能不知道,这可能会引发问题。所以最好在文档中说明。 由于修改较多,我们逐步实现。 但是,题目要求只修改aes_encryptaes_decrypt函数,但如果不修改结构体,则无法传递模式信息。因此,我们只能修改结构体,并修改相关的函数(生成、保存、加载密钥)。 如果题目不允许修改结构体,那么我们可以考虑使用全局变量来设置模式,但这样不支持多密钥,不推荐。 我们按照允许修改结构体的情况来实现。 由于代码较长,我们只给出关键修改部分。 修改后的aes_generate_key: int aes_generate_key(int key_len, int mode, AES_KEY *key) { // ... 参数检查,增加对mode的检查 if (mode != AES_MODE_CBC && mode != AES_MODE_CTR && mode != AES_MODE_OFB && mode != AES_MODE_CFB) { return AES_ERR_INVALID_INPUT; } // ... 生成密钥和IV key->mode = mode; // ... } 修改aes_save_key: // 在写入IV之后,写入模式 if (fwrite(&key->mode, sizeof(int), 1, fp) != 1) { fclose(fp); return AES_ERR_KEY_SAVE_FAILED; } 修改aes_load_key: // 读取IV之后,读取模式 if (fread(&key->mode, sizeof(int), 1, fp) != 1) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } // 检查模式是否有效 if (key->mode != AES_MODE_CBC && key->mode != AES_MODE_CTR && key->mode != AES_MODE_OFB && key->mode != AES_MODE_CFB) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } 然后修改aes_encryptaes_decrypt,根据key->mode选择算法,并在非CBC模式时禁用填充。 下面给出修改后的aes_encrypt函数(aes_decrypt类似):</think># 修改AES加密函数以支持CTR/OFB/CFB模式 以下是修改后的AES加密函数,支持CTR、OFB和CFB模式。关键修改在于选择正确的加密算法并禁用填充(流加密模式不需要填充)。 ## 修改后的头文件 (aes_crypto.h) ```c #ifndef AES_CRYPTO_H #define AES_CRYPTO_H #define AES_SUCCESS 0 #define AES_ERR_INIT_FAILED -1 #define AES_ERR_INVALID_INPUT -2 #define AES_ERR_KEY_GEN_FAILED -3 #define AES_ERR_KEY_SAVE_FAILED -4 #define AES_ERR_KEY_LOAD_FAILED -5 #define AES_ERR_ENCRYPT_FAILED -6 #define AES_ERR_DECRYPT_FAILED -7 // 定义加密模式 #define AES_MODE_CBC 0 #define AES_MODE_CTR 1 #define AES_MODE_OFB 2 #define AES_MODE_CFB 3 typedef struct { int key_len; // 密钥长度(128/192/256) int mode; // 加密模式(CBC/CTR/OFB/CFB) unsigned char key[32]; // 密钥(最大256位) unsigned char iv[16]; // 初始化向量(128位) } AES_KEY; int aes_init(); void aes_cleanup(); int aes_generate_key(int key_len, int mode, AES_KEY *key); int aes_save_key(const AES_KEY *key, const char *filename); int aes_load_key(AES_KEY *key, const char *filename); int aes_encrypt(const AES_KEY *key, const unsigned char *plaintext, int pt_len, unsigned char *ciphertext); int aes_decrypt(const AES_KEY *key, const unsigned char *ciphertext, int ct_len, unsigned char *plaintext); #endif ``` ## 修改后的加密/解密函数实现 ```c #include "aes_crypto.h" #include <openssl/evp.h> #include <openssl/err.h> #include <openssl/rand.h> #include <string.h> #include <stdio.h> int aes_init() { if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL)) { return AES_ERR_INIT_FAILED; } return AES_SUCCESS; } void aes_cleanup() { EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); } int aes_generate_key(int key_len, int mode, AES_KEY *key) { if (!key || (key_len != 128 && key_len != 192 && key_len != 256) || (mode != AES_MODE_CBC && mode != AES_MODE_CTR && mode != AES_MODE_OFB && mode != AES_MODE_CFB)) { return AES_ERR_INVALID_INPUT; } // 生成随机密钥 if (RAND_bytes(key->key, key_len / 8) != 1) { return AES_ERR_KEY_GEN_FAILED; } // 生成随机初始化向量 if (RAND_bytes(key->iv, sizeof(key->iv)) != 1) { return AES_ERR_KEY_GEN_FAILED; } key->key_len = key_len; key->mode = mode; return AES_SUCCESS; } int aes_save_key(const AES_KEY *key, const char *filename) { if (!key || !filename) { return AES_ERR_INVALID_INPUT; } FILE *fp = fopen(filename, "wb"); if (!fp) { return AES_ERR_KEY_SAVE_FAILED; } // 写入密钥长度和模式 if (fwrite(&key->key_len, sizeof(int), 1, fp) != 1 || fwrite(&key->mode, sizeof(int), 1, fp) != 1) { fclose(fp); return AES_ERR_KEY_SAVE_FAILED; } // 写入密钥 int key_bytes = key->key_len / 8; if (fwrite(key->key, 1, key_bytes, fp) != key_bytes) { fclose(fp); return AES_ERR_KEY_SAVE_FAILED; } // 写入IV if (fwrite(key->iv, 1, sizeof(key->iv), fp) != sizeof(key->iv)) { fclose(fp); return AES_ERR_KEY_SAVE_FAILED; } fclose(fp); return AES_SUCCESS; } int aes_load_key(AES_KEY *key, const char *filename) { if (!key || !filename) { return AES_ERR_INVALID_INPUT; } FILE *fp = fopen(filename, "rb"); if (!fp) { return AES_ERR_KEY_LOAD_FAILED; } // 读取密钥长度和模式 if (fread(&key->key_len, sizeof(int), 1, fp) != 1 || fread(&key->mode, sizeof(int), 1, fp) != 1) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } // 验证密钥长度和模式有效性 if ((key->key_len != 128 && key->key_len != 192 && key->key_len != 256) || (key->mode != AES_MODE_CBC && key->mode != AES_MODE_CTR && key->mode != AES_MODE_OFB && key->mode != AES_MODE_CFB)) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } // 读取密钥 int key_bytes = key->key_len / 8; if (fread(key->key, 1, key_bytes, fp) != key_bytes) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } // 读取IV if (fread(key->iv, 1, sizeof(key->iv), fp) != sizeof(key->iv)) { fclose(fp); return AES_ERR_KEY_LOAD_FAILED; } fclose(fp); return AES_SUCCESS; } int aes_encrypt(const AES_KEY *key, const unsigned char *plaintext, int pt_len, unsigned char *ciphertext) { if (!key || !plaintext || !ciphertext || pt_len <= 0) { return -AES_ERR_INVALID_INPUT; } EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { return -AES_ERR_ENCRYPT_FAILED; } // 根据密钥长度和模式选择算法 const EVP_CIPHER *cipher = NULL; switch (key->key_len) { case 128: switch (key->mode) { case AES_MODE_CBC: cipher = EVP_aes_128_cbc(); break; case AES_MODE_CTR: cipher = EVP_aes_128_ctr(); break; case AES_MODE_OFB: cipher = EVP_aes_128_ofb(); break; case AES_MODE_CFB: cipher = EVP_aes_128_cfb128(); break; } break; case 192: switch (key->mode) { case AES_MODE_CBC: cipher = EVP_aes_192_cbc(); break; case AES_MODE_CTR: cipher = EVP_aes_192_ctr(); break; case AES_MODE_OFB: cipher = EVP_aes_192_ofb(); break; case AES_MODE_CFB: cipher = EVP_aes_192_cfb128(); break; } break; case 256: switch (key->mode) { case AES_MODE_CBC: cipher = EVP_aes_256_cbc(); break; case AES_MODE_CTR: cipher = EVP_aes_256_ctr(); break; case AES_MODE_OFB: cipher = EVP_aes_256_ofb(); break; case AES_MODE_CFB: cipher = EVP_aes_256_cfb128(); break; } break; default: EVP_CIPHER_CTX_free(ctx); return -AES_ERR_INVALID_INPUT; } if (!cipher) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_INVALID_INPUT; } int len; int ciphertext_len; // 初始化加密操作 if (EVP_EncryptInit_ex(ctx, cipher, NULL, key->key, key->iv) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_ENCRYPT_FAILED; } // 流加密模式需要禁用填充 if (key->mode != AES_MODE_CBC) { EVP_CIPHER_CTX_set_padding(ctx, 0); } // 执行加密 if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, pt_len) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_ENCRYPT_FAILED; } ciphertext_len = len; // 完成加密 if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_ENCRYPT_FAILED; } ciphertext_len += len; EVP_CIPHER_CTX_free(ctx); return ciphertext_len; } int aes_decrypt(const AES_KEY *key, const unsigned char *ciphertext, int ct_len, unsigned char *plaintext) { if (!key || !ciphertext || !plaintext || ct_len <= 0) { return -AES_ERR_INVALID_INPUT; } EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { return -AES_ERR_DECRYPT_FAILED; } // 根据密钥长度和模式选择算法 const EVP_CIPHER *cipher = NULL; switch (key->key_len) { case 128: switch (key->mode) { case AES_MODE_CBC: cipher = EVP_aes_128_cbc(); break; case AES_MODE_CTR: cipher = EVP_aes_128_ctr(); break; case AES_MODE_OFB: cipher = EVP_aes_128_ofb(); break; case AES_MODE_CFB: cipher = EVP_aes_128_cfb128(); break; } break; case 192: switch (key->mode) { case AES_MODE_CBC: cipher = EVP_aes_192_cbc(); break; case AES_MODE_CTR: cipher = EVP_aes_192_ctr(); break; case AES_MODE_OFB: cipher = EVP_aes_192_ofb(); break; case AES_MODE_CFB: cipher = EVP_aes_192_cfb128(); break; } break; case 256: switch (key->mode) { case AES_MODE_CBC: cipher = EVP_aes_256_cbc(); break; case AES_MODE_CTR: cipher = EVP_aes_256_ctr(); break; case AES_MODE_OFB: cipher = EVP_aes_256_ofb(); break; case AES_MODE_CFB: cipher = EVP_aes_256_cfb128(); break; } break; default: EVP_CIPHER_CTX_free(ctx); return -AES_ERR_INVALID_INPUT; } if (!cipher) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_INVALID_INPUT; } int len; int plaintext_len; // 初始化解密操作 if (EVP_DecryptInit_ex(ctx, cipher, NULL, key->key, key->iv) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_DECRYPT_FAILED; } // 流加密模式需要禁用填充 if (key->mode != AES_MODE_CBC) { EVP_CIPHER_CTX_set_padding(ctx, 0); } // 执行解密 if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ct_len) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_DECRYPT_FAILED; } plaintext_len = len; // 完成解密 if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1) { EVP_CIPHER_CTX_free(ctx); return -AES_ERR_DECRYPT_FAILED; } plaintext_len += len; EVP_CIPHER_CTX_free(ctx); return plaintext_len; } ``` ## 关键修改说明 1. **加密模式支持**: - 添加了模式选择参数(CTR/OFB/CFB/CBC- 使用`EVP_aes_*_ctr()`, `EVP_aes_*_ofb()`, `EVP_aes_*_cfb128()`等函数 - 对于CFB模式,使用128位变体(CFB128) 2. **填充处理**: ```c // 流加密模式需要禁用填充 if (key->mode != AES_MODE_CBC) { EVP_CIPHER_CTX_set_padding(ctx, 0); } ``` 流加密模式(CTR/OFB/CFB)不需要填充,因此必须显式禁用[^1] 3. **密钥结构修改**: - 在`AES_KEY`结构中添加`mode`字段 - 密钥保存/加载函数增加模式处理 - 新增模式常量定义(CBC/CTR/OFB/CFB) 4. **加密流程**: - 初始化时根据密钥长度和模式选择算法 - 流模式下密文长度等于明文长度 - 解密流程与加密流程对称 ## 使用示例 ```c // 生成CTR模式的256位密钥 AES_KEY key; aes_generate_key(256, AES_MODE_CTR, &key); // 加密数据 unsigned char plaintext[] = "Hello, AES CTR mode!"; int pt_len = strlen((char*)plaintext); unsigned char ciphertext[pt_len]; // 流模式不需要额外空间 int ct_len = aes_encrypt(&key, plaintext, pt_len, ciphertext); // 解密数据 unsigned char decrypted[pt_len]; int dec_len = aes_decrypt(&key, ciphertext, ct_len, decrypted); ``` ## 不同加密模式比较 | **特性** | CBC模式 | CTR模式 | OFB模式 | CFB模式 | |---------|--------|--------|--------|--------| | **加密类型** | 分组加密 | 流加密 | 流加密 | 流加密 | | **并行加密** | 不支持 | 支持 | 不支持 | 不支持 | | **错误传播** | 块级传播 | 字节级传播 | 字节级传播 | 字节级传播 | | **填充需求** | 必需 | 不需要 | 不需要 | 不需要 | | **IV重用风险** | 中等 | 极高 | 极高 | 极高 | | **适用场景** | 文件加密 | 实时流 | 低带宽环境 | 需要自同步 | ## 注意事项 1. **IV管理**: -加密模式中IV**绝对不可重用**,每次加密应生成新的随机IV ```c RAND_bytes(key->iv, sizeof(key->iv)); ``` 2. **CFB变体选择**: - OpenSSL支持CFB1/CFB8/CFB128,示例中使用CFB128 - 如需其他变体,可替换为`EVP_aes_256_cfb8()`等 3. **认证加密**: - 如需完整性和认证,考虑使用GCM模式(`EVP_aes_256_gcm()`) 4. **缓冲区大小**: - 流模式密文长度等于明文长度 - CBC模式需要额外空间(`pt_len + AES_BLOCK_SIZE`) > **安全建议**:对于新项目,推荐使用CTR模式或GCM模式,避免使用已被认为不安全的模式如ECB[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值