为何使用EVP时aes_128_cbc加密解密正常,aes_128_gcm却失效?
解决AES-128-GCM无加密输出的问题
嘿,我明白你遇到的问题了——CBC模式跑的好好的,换成GCM就没输出,这是因为GCM属于**AEAD(认证加密带关联数据)**模式,和CBC的处理流程有不少关键差异,你之前的代码肯定没适配这些差异。下面我给你拆解原因,再附上修正后的完整代码:
核心问题分析
- GCM必须处理认证标签:和CBC只输出密文不同,GCM会生成一个16字节的认证标签(用于解密时验证数据完整性),你必须主动获取这个标签并和密文一起保存/输出,否则等于没拿到完整的加密结果。
- 加密流程的差异:GCM是流加密模式,不需要像CBC那样做填充,
EVP_EncryptFinal_ex不会输出额外数据,只是完成认证计算,而且必须显式调用接口获取认证标签。 - IV长度的隐性建议:虽然你用16字节IV也能工作,但GCM标准推荐用12字节(96位)IV,这样OpenSSL会采用更高效安全的计数器生成方式,不过16字节也能兼容。
修正后的完整代码
#include <stdlib.h> #include <openssl/evp.h> #include <string.h> #include <stdio.h> int main() { EVP_CIPHER_CTX *ctx; char key[] = "somerandomkey123"; // 16字节,符合AES-128要求 unsigned char iv[] = "my16byteiv123456"; // 可替换为12字节IV(推荐) unsigned char output[1024]; // 预留足够空间存密文+标签(明文长度+16字节) char input[] = "Message here."; int len, ciphertext_len, tag_len = 16; // GCM标签默认16字节 // 初始化加密上下文 if (!(ctx = EVP_CIPHER_CTX_new())) { perror("EVP_CIPHER_CTX_new failed"); return 1; } // 初始化AES-128-GCM加密操作 if (EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL) != 1) { perror("EVP_EncryptInit_ex failed"); EVP_CIPHER_CTX_free(ctx); return 1; } // 设置密钥和IV if (EVP_EncryptInit_ex(ctx, NULL, NULL, (unsigned char*)key, iv) != 1) { perror("EVP_EncryptInit_ex (key/iv) failed"); EVP_CIPHER_CTX_free(ctx); return 1; } // 加密明文 if (EVP_EncryptUpdate(ctx, output, &len, (unsigned char*)input, strlen(input)) != 1) { perror("EVP_EncryptUpdate failed"); EVP_CIPHER_CTX_free(ctx); return 1; } ciphertext_len = len; // GCM模式下,EVP_EncryptFinal_ex仅完成认证计算,无额外密文输出 if (EVP_EncryptFinal_ex(ctx, output + len, &len) != 1) { perror("EVP_EncryptFinal_ex failed"); EVP_CIPHER_CTX_free(ctx); return 1; } ciphertext_len += len; // 此处len通常为0,因为GCM无需填充 // 获取认证标签,追加到密文末尾 if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_len, output + ciphertext_len) != 1) { perror("EVP_CTRL_GCM_GET_TAG failed"); EVP_CIPHER_CTX_free(ctx); return 1; } ciphertext_len += tag_len; // 输出加密结果(密文+标签) printf("加密后总长度:%d\n", ciphertext_len); printf("加密数据(十六进制):"); for (int i = 0; i < ciphertext_len; i++) { printf("%02x", output[i]); } printf("\n"); // 写入二进制文件示例(可选) // FILE *binfile = fopen("encrypted.bin", "wb"); // if (binfile) { // fwrite(output, 1, ciphertext_len, binfile); // fclose(binfile); // } // 清理资源 EVP_CIPHER_CTX_free(ctx); return 0; }
关键差异点说明
- 标签获取步骤:这是你之前代码缺失的核心——必须调用
EVP_CIPHER_CTX_ctrl传入EVP_CTRL_GCM_GET_TAG参数,把认证标签写入输出缓冲区的密文后面,否则只会得到空的或不完整的结果。 - Final_ex的作用:GCM的
EVP_EncryptFinal_ex不产生密文,只是完成认证计算,所以返回的len通常为0。 - 缓冲区预留:要确保输出缓冲区能容纳明文长度+16字节标签,避免数据溢出。
如果后续要解密,也要对应调整流程:解密时需先传入密文,再调用EVP_CTRL_GCM_SET_TAG设置标签,最后验证数据完整性。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

