使用OpenSSl库实现AES-GCM-128算法(C语言)

twd-log / 2024-11-13 / 原文

在C语言中使用OpenSSL库实现AES-GCM-128算法,并生成GMAC(Galois Message Authentication Code)消息认证码,通过以下步骤完成:

  1. 初始化加密环境:创建一个EVP_CIPHER_CTX结构体,用于存储加密过程中的所有必要信息。
  2. 设置加密算法:指定使用AES-GCM模式,以及密钥和IV(初始化向量)。
  3. 处理附加认证数据(AAD):如果有不需要加密但需要进行认证的数据,可以在加密之前设置。
  4. 加密数据:将明文数据进行加密,得到密文。
  5. 生成GMAC:在加密完成后,通过EVP_CIPHER_CTX_ctrl函数获取GMAC。
  6. 初始化解密环境:与加密类似,创建并初始化EVP_CIPHER_CTX结构体。
  7. 设置解密算法:指定使用AES-GCM模式,以及密钥和IV。
  8. 处理附加认证数据(AAD):与加密时相同,设置相同的AAD数据。
  9. 解密数据:将密文数据进行解密,得到明文。
  10. 验证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;

}

运行结果: