在C语言中使用OpenSSL库实现AES-GCM-128算法,并生成GMAC(Galois Message Authentication Code)消息认证码,通过以下步骤完成:
- 初始化加密环境:创建一个EVP_CIPHER_CTX结构体,用于存储加密过程中的所有必要信息。
- 设置加密算法:指定使用AES-GCM模式,以及密钥和IV(初始化向量)。
- 处理附加认证数据(AAD):如果有不需要加密但需要进行认证的数据,可以在加密之前设置。
- 加密数据:将明文数据进行加密,得到密文。
- 生成GMAC:在加密完成后,通过EVP_CIPHER_CTX_ctrl函数获取GMAC。
- 初始化解密环境:与加密类似,创建并初始化EVP_CIPHER_CTX结构体。
- 设置解密算法:指定使用AES-GCM模式,以及密钥和IV。
- 处理附加认证数据(AAD):与加密时相同,设置相同的AAD数据。
- 解密数据:将密文数据进行解密,得到明文。
- 验证GMAC:在解密完成后,通过EVP_CIPHER_CTX_ctrl函数设置预期的GMAC,并调用EVP_DecryptFinal_ex函数来验证GMAC是否正确。
具体实现如下:
加密函数
int aes_gcm_encrypt(const unsigned char* plaintext, int plaintext_len, const unsigned char* key,
const unsigned char* iv, const unsigned char* aad, int aad_len,
unsigned char* ciphertext, unsigned char* gmac, int gmac_len)
{
EVP_CIPHER_CTX* ctx;
int len;
int ciphertext_len;
//创建初始化加密上下文
if (!(ctx = EVP_CIPHER_CTX_new()))
{
fprintf(stderr, "Error creating cipher context.\n");
return 0;
}
//使用aes_128_gcm算法初始化加密操作
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, key, iv))
{
fprintf(stderr, "Error initialising encryption.\n");
return 0;
}
//设置附加认证数据(AAD)
if (aad_len > 0)
{
if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
{
fprintf(stderr, "Error setting AAD.\n");
return 0;
}
}
//加密数据
if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
{
fprintf(stderr, "Error encrypting plaintext.\n");
return 0;
}
ciphertext_len = len;
//结束加密操作
if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
{
fprintf(stderr, "Error finalising encryption.\n");
return 0;
}
ciphertext_len += len;
//获取GMAC消息认证码
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, gmac_len, gmac))
{
fprintf(stderr, "Error getting GMAC.\n");
return 0;
}
//释放ctx结构体
EVP_CIPHER_CTX_free(ctx);
//返回加密后的长度
return ciphertext_len;
}
解密函数
int aes_gcm_decrypt(const unsigned char* ciphertext, int ciphertext_len, const unsigned char* key,
const unsigned char* iv, const unsigned char* aad, int aad_len,
unsigned char* plaintext, const unsigned char* gmac, int gmac_len)
{
EVP_CIPHER_CTX* ctx;
int len;
int plaintext_len;
int ret;
//创建初始化解密上下文
if (!(ctx = EVP_CIPHER_CTX_new()))
{
fprintf(stderr, "Error creating cipher context.\n");
return 0;
}
//使用aes_128_gcm算法初始化解密操作
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, key, iv))
{
fprintf(stderr, "Error initialising decryption.\n");
return 0;
}
//设置附加认证数据(AAD)
if (aad_len > 0) {
if (1 != EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
{
fprintf(stderr, "Error setting AAD.\n");
return 0;
}
}
//解密数据
if (1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
{
fprintf(stderr, "Error decrypting ciphertext.\n");
return 0;
}
plaintext_len = len;
//设置GMAC消息认证码
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, gmac_len, (void*)gmac))
{
fprintf(stderr, "Error setting tag.\n");
return 0;
}
//结束解密操作,验证附加数据的完整性
ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
if (ret > 0)
{
plaintext_len += len;
}
else
{
fprintf(stderr, "Error finalising decryption.\n");
return 0;
}
//释放ctx结构体
EVP_CIPHER_CTX_free(ctx);
//返回明文长度
return plaintext_len;
}
主函数
int main()
{
unsigned char key[AES_KEY_SIZE];
unsigned char iv[GCM_IV_SIZE];
unsigned char aad[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
unsigned char plaintext[] = {0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12};
unsigned char* ciphertext;
unsigned char gmac[GCM_TAG_SIZE];
int ciphertext_len;
//初始化密钥和 IV
memset(key, 0x00, sizeof(key));
memset(iv, 0x00, sizeof(iv));
//RAND_bytes(key, sizeof(key));
//RAND_bytes(iv, sizeof(iv));
//为密文存储开辟空间
ciphertext = OPENSSL_malloc(sizeof(plaintext) + EVP_MAX_BLOCK_LENGTH);
if (!ciphertext)
{
fprintf(stderr, "Could not allocate memory for ciphertext.\n");
return 1;
}
ciphertext_len = aes_gcm_encrypt(plaintext, sizeof(plaintext), key, iv, aad, sizeof(aad), ciphertext, gmac, GCM_TAG_SIZE);
if (ciphertext_len > 0)
{
printf("Ciphertext is:\n");
xprint(ciphertext, ciphertext_len);
printf("GMAC is:\n");
xprint(gmac, sizeof(gmac));
unsigned char decryptedtext[128];
int decryptedtext_len = aes_gcm_decrypt(ciphertext, ciphertext_len, key, iv, aad, sizeof(aad), decryptedtext, gmac, GCM_TAG_SIZE);
if (decryptedtext_len > 0)
{
printf("Decrypted text is:\n");
xprint(decryptedtext, decryptedtext_len);
}
else
{
fprintf(stderr, "Error decrypting plaintext.\n");
}
}
else
{
fprintf(stderr, "Error encrypting plaintext.\n");
}
//释放空间
OPENSSL_free(ciphertext);
return 0;
}
运行结果: