You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:26:55