AES-256-CBC加密解密函数动态IV实现:IV附加密文前的方法
AES-CBC加密解密:动态IV前缀实现方案
你之前尝试直接在加密函数末尾附加IV到密文开头但没生效,核心问题是加密后的密文已经占据了ciphertext缓冲区的起始位置,直接写入IV会覆盖部分密文,而且没有调整缓冲区数据的位置。以下是正确的修改方案:
一、加密函数修改
需要先把加密生成的密文向后偏移16字节(AES块大小,即IV长度),再将IV写入缓冲区的前16字节,同时更新返回的总长度。
修改后的代码:
#include <string.h> // 需引入string.h用于memmove和memcpy int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext) { EVP_CIPHER_CTX *ctx; int len; int ciphertext_len; const int IV_LEN = 16; // AES-CBC的IV固定为16字节 /* Create and initialise the context */ if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors(); /* Initialise encryption operation */ if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) handleErrors(); /* Encrypt plaintext */ if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) handleErrors(); ciphertext_len = len; /* Finalise encryption */ if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors(); ciphertext_len += len; /* Clean up */ EVP_CIPHER_CTX_free(ctx); // --- 关键修改:将IV作为前缀附加到密文前 --- // 1. 把已生成的密文向后移动16字节,给IV腾出空间 memmove(ciphertext + IV_LEN, ciphertext, ciphertext_len); // 2. 将IV复制到缓冲区的前16字节 memcpy(ciphertext, iv, IV_LEN); // 3. 更新总长度:密文长度 + IV长度 ciphertext_len += IV_LEN; return ciphertext_len; }
关键改动说明:
- 使用
memmove而非memcpy移动密文:因为源地址和目标地址有重叠,memmove能安全处理这种情况 - 必须先移动密文再写入IV,否则会覆盖未移动的密文数据
- 返回的长度要加上IV的16字节,调用方需要知道最终输出的总长度
二、解密函数修改
解密时需要先从密文缓冲区的前16字节提取IV,再用这个IV解密剩下的密文内容,同时注意传入的密文长度要减去IV的16字节。
修改后的代码:
#include <string.h> int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *plaintext) { EVP_CIPHER_CTX *ctx; int len; int plaintext_len; const int IV_LEN = 16; unsigned char extracted_iv[IV_LEN]; // 校验密文长度至少包含IV if(ciphertext_len < IV_LEN) handleErrors(); /* Create and initialise the context */ if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors(); // --- 关键修改:提取密文前缀的IV --- memcpy(extracted_iv, ciphertext, IV_LEN); /* Initialise decryption operation,使用提取的IV */ if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, extracted_iv)) handleErrors(); /* Decrypt ciphertext(跳过前16字节的IV) */ if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext + IV_LEN, ciphertext_len - IV_LEN)) handleErrors(); plaintext_len = len; /* Finalise decryption */ if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors(); plaintext_len += len; /* Clean up */ EVP_CIPHER_CTX_free(ctx); return plaintext_len; }
关键改动说明:
- 新增了
extracted_iv数组存储从密文前缀提取的IV,不再依赖传入的iv参数(可以移除原函数的iv参数,如上面代码所示) - 解密时传入的密文起始位置是
ciphertext + IV_LEN,长度是原长度减去16字节 - 先校验密文长度是否足够,避免越界访问
注意事项
- 调用加密函数时,传入的
ciphertext缓冲区大小必须至少为plaintext_len + AES_BLOCK_SIZE + IV_LEN(AES_BLOCK_SIZE为16,用于加密填充),否则会出现缓冲区溢出 - IV必须是随机生成的(可以用
RAND_bytes(iv, IV_LEN)生成),这是保证CBC模式安全性的关键,不能使用固定IV
内容的提问来源于stack exchange,提问作者georgetil191
相关产品推荐
相关产品推荐

