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; }
关键步骤说明
Nonce(IV)的作用:
- CCM模式必须使用Nonce,它相当于一次性的初始化向量,同一个密钥下绝对不能重复使用相同的Nonce,否则会导致整个加密体系被破解。推荐用
gcry_randomize生成强随机的Nonce。 - Nonce长度推荐12字节(NIST标准),范围是7-13字节,长度越短,计数器部分越长,但12字节是平衡安全性和效率的最优选择。
- CCM模式必须使用Nonce,它相当于一次性的初始化向量,同一个密钥下绝对不能重复使用相同的Nonce,否则会导致整个加密体系被破解。推荐用
认证标签的处理:
- 加密时,Libgcrypt会生成一个认证标签,用于验证密文和AAD的完整性与真实性。需要用
gcry_cipher_gettag获取标签,通常和密文一起存储/传输。 - 解密时,必须调用
gcry_cipher_checktag验证标签,如果验证失败,说明密文或AAD被篡改,此时解密得到的明文不可信。
- 加密时,Libgcrypt会生成一个认证标签,用于验证密文和AAD的完整性与真实性。需要用
附加认证数据(AAD):
- 如果需要对一些不需要加密但需要认证的数据(比如消息头)进行保护,可以用
gcry_cipher_authenticate传入AAD,加密和解密时必须传入完全相同的AAD才能通过标签验证。
- 如果需要对一些不需要加密但需要认证的数据(比如消息头)进行保护,可以用
编译与运行
编译时需要链接Libgcrypt库:
gcc -o aes_ccm aes_ccm.c -lgcrypt
运行后,你会看到明文被正确加密和解密,且标签验证通过。如果篡改密文或标签,解密时会提示验证失败。
内容的提问来源于stack exchange,提问作者Tomi Jerenko
相关产品推荐
相关产品推荐

