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

Libgcrypt中AES128-CCM模式加解密问题及代码求助

在Libgcrypt中实现AES-128 CCM模式加解密的完整方案

CCM是认证加密(AEAD)模式,和普通AES模式(比如ECB/CBC)不同,它需要三个关键要素:Nonce(IV)、认证标签,以及可选的附加认证数据(AAD)。你的代码缺失了这些核心步骤,所以无法正常工作。下面是完整的可运行代码,以及详细的步骤说明:

完整示例代码

#include <gcrypt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define KEY_LEN 16          // AES-128密钥长度
#define NONCE_LEN 12        // CCM推荐Nonce长度(NIST标准,7-13字节均可)
#define TAG_LEN 16          // 认证标签长度(可选4/6/8/10/12/14/16字节)

int main() {
    gcry_error_t err;
    gcry_cipher_hd_t hd;
    char *key = "1234567890123456";
    char *plainText = "VNiJkPzAWPFm1234";
    size_t messageSize = strlen(plainText);

    // 分配内存:密文(和明文同长)、恢复明文、Nonce、认证标签
    char *cipherText = malloc(messageSize);
    char *recoveredText = malloc(messageSize);
    unsigned char nonce[NONCE_LEN];
    unsigned char tag[TAG_LEN];

    if (!cipherText || !recoveredText) {
        fprintf(stderr, "内存分配失败\n");
        return 1;
    }

    // -------------------------- 加密流程 --------------------------
    // 1. 打开AES-128 CCM模式上下文
    err = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CCM, 0);
    if (err) {
        fprintf(stderr, "打开上下文失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 2. 设置密钥
    err = gcry_cipher_setkey(hd, key, KEY_LEN);
    if (err) {
        fprintf(stderr, "设置密钥失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 3. 生成安全随机Nonce(同一个密钥下绝对不能重复使用Nonce!)
    gcry_randomize(nonce, NONCE_LEN, GCRY_STRONG_RANDOM);

    // 4. 设置Nonce(IV)
    err = gcry_cipher_setiv(hd, nonce, NONCE_LEN);
    if (err) {
        fprintf(stderr, "设置Nonce失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 5. 设置认证标签长度
    err = gcry_cipher_ctrl(hd, GCRYCTL_SET_TAGLEN, TAG_LEN, NULL);
    if (err) {
        fprintf(stderr, "设置标签长度失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // (可选)如果有附加认证数据(AAD),在这里调用:
    // char *aad = "附加数据";
    // err = gcry_cipher_authenticate(hd, aad, strlen(aad));
    // if (err) { ... }

    // 6. 加密明文得到密文
    err = gcry_cipher_encrypt(hd, cipherText, messageSize, plainText, messageSize);
    if (err) {
        fprintf(stderr, "加密失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 7. 获取生成的认证标签
    err = gcry_cipher_gettag(hd, tag, TAG_LEN);
    if (err) {
        fprintf(stderr, "获取标签失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 关闭加密上下文
    gcry_cipher_close(hd);

    // -------------------------- 解密流程 --------------------------
    // 1. 重新打开上下文(或使用gcry_cipher_reset重置)
    err = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CCM, 0);
    if (err) {
        fprintf(stderr, "打开解密上下文失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 2. 设置相同的密钥
    err = gcry_cipher_setkey(hd, key, KEY_LEN);
    if (err) {
        fprintf(stderr, "设置解密密钥失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 3. 设置和加密时完全相同的Nonce
    err = gcry_cipher_setiv(hd, nonce, NONCE_LEN);
    if (err) {
        fprintf(stderr, "设置解密Nonce失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 4. 设置相同的标签长度
    err = gcry_cipher_ctrl(hd, GCRYCTL_SET_TAGLEN, TAG_LEN, NULL);
    if (err) {
        fprintf(stderr, "设置解密标签长度失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // (可选)如果加密时用了AAD,解密时必须传入相同的AAD:
    // err = gcry_cipher_authenticate(hd, aad, strlen(aad));
    // if (err) { ... }

    // 5. 解密密文得到明文
    err = gcry_cipher_decrypt(hd, recoveredText, messageSize, cipherText, messageSize);
    if (err) {
        fprintf(stderr, "解密失败: %s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 6. 验证认证标签(这一步是CCM模式安全的核心,必须做!)
    err = gcry_cipher_checktag(hd, tag, TAG_LEN);
    if (err) {
        fprintf(stderr, "标签验证失败:密文或AAD被篡改!%s\n", gcry_strerror(err));
        goto cleanup;
    }

    // 输出结果
    printf("原始明文: %s\n", plainText);
    printf("解密明文: %s\n", recoveredText);

cleanup:
    // 清理资源
    if (hd) gcry_cipher_close(hd);
    free(cipherText);
    free(recoveredText);
    return err ? 1 : 0;
}

关键步骤说明

  1. Nonce(IV)的作用:

    • CCM模式必须使用Nonce,它相当于一次性的初始化向量,同一个密钥下绝对不能重复使用相同的Nonce,否则会导致整个加密体系被破解。推荐用gcry_randomize生成强随机的Nonce。
    • Nonce长度推荐12字节(NIST标准),范围是7-13字节,长度越短,计数器部分越长,但12字节是平衡安全性和效率的最优选择。
  2. 认证标签的处理:

    • 加密时,Libgcrypt会生成一个认证标签,用于验证密文和AAD的完整性与真实性。需要用gcry_cipher_gettag获取标签,通常和密文一起存储/传输。
    • 解密时,必须调用gcry_cipher_checktag验证标签,如果验证失败,说明密文或AAD被篡改,此时解密得到的明文不可信。
  3. 附加认证数据(AAD):

    • 如果需要对一些不需要加密但需要认证的数据(比如消息头)进行保护,可以用gcry_cipher_authenticate传入AAD,加密和解密时必须传入完全相同的AAD才能通过标签验证。

编译与运行

编译时需要链接Libgcrypt库:

gcc -o aes_ccm aes_ccm.c -lgcrypt

运行后,你会看到明文被正确加密和解密,且标签验证通过。如果篡改密文或标签,解密时会提示验证失败。

内容的提问来源于stack exchange,提问作者Tomi Jerenko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:21:44