公司以往开发的软件,大部分的数据交互都采用明文传输的方式,新的产品数据传输是暴露在公网上的,为了防止数据被恶意篡改,特引入AES-GCM加密算法,并使用开源的openssl实现。下面针对此算法做一个简单的介绍。
1.AES-GCM 加密加密算法简介
AES-GCM(高级加密标准-伽罗瓦/计数器模式)是一种对称加密算法,它结合了块加密和消息认证码(MAC)的功能,提供数据加密和完整性验证。AES-GCM 模式因其高效和安全性而被广泛应用于各种安全通信协议中,如 TLS(传输层安全)和 IPsec(互联网协议安全)。
AES-GCM 的工作原理如下:
加密:使用 AES 块密码的计数器模式对数据进行加密。计数器模式通过将一个计数器与 AES 算法结合,生成伪随机比特流,然后将这个比特流与明文进行异或操作来加密数据。
认证:使用伽罗瓦/计数器模式(GCM)生成一个消息认证码(TAG),用于验证数据的完整性和真实性。GCM 模式利用有限域的乘法运算来计算 TAG。
AES-GCM 的主要优点包括:
高效:AES-GCM 可以并行处理,因此加密和解密速度较快。安全性:AES-GCM 提供了强大的数据加密和完整性保护,能够抵御已知的许多攻击。简洁:AES-GCM 的实现相对简单,易于理解和实现。
使用 AES-GCM 时需要注意以下几点:
密钥管理:密钥必须保密,并且每个消息应该使用唯一的初始化向量(IV)。错误处理:如果解密过程中 MAC 验证失败,应该丢弃消息并报告错误,以防止侧信道攻击。性能:AES-GCM 的性能取决于硬件和软件实现,因此在选择实现时需要考虑性能要求。
AES-GCM 是一种强大的加密模式,适用于需要高效和安全数据传输的应用场景。正确实现和使用 AES-GCM 可以确保数据的安全性和完整性。
2.openssl下载和vs2022配置
2.1 openssl 下载和安装
下载地址:https://slproweb.com/products/Win32OpenSSL.html

下载安装在自己的电脑上:安装目录文件夹说明
| 目录 | 说明 |
| bin | 此目录包含需要的动态库文件libssl-3.dll libcrypto-3.dll |
| include | 包含编程所需要的头文件 |
| lib | 包含两个目录,vs选择vc/x86目录下的lib文件MD是动态lib |
2.2 vs2022新建工程并配置openssl
项目–>配置属性-》VC++–》包含目录———》添加头文件对应的目录项目–>配置属性-》VC++–》库目录————》添加库文件目录项目–>配置属性-》高级–》字符集–》使用多字符集项目–>配置属性-》C/C++–》预处理器–》预处理器定义–》添加 _CRT_SECURE_NO_WARNINGS项目–>配置属性-》连接器–》输入–》附加依赖项—》添加 libssl.lib libcrypto.lib ws2_32.lib
3.代码示例
aes_gcm_128.h
#ifndef AES_GCM_128_H
#define AES_GCM_128_H
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <stdint.h>
#include <stdlib.h>
#define AES_128_KEY_SIZE 16
#define GCM_IV_SIZE 12
#define GCM_TAG_SIZE 16
// AES-GCM加密函数
int aes_gcm_encrypt(const uint8_t* plaintext, size_t plaintext_len,
const uint8_t* key, const uint8_t* iv,
uint8_t* ciphertext, uint8_t* tag);
// AES-GCM解密函数
int aes_gcm_decrypt(const uint8_t* ciphertext, size_t ciphertext_len,
const uint8_t* key, const uint8_t* iv, const uint8_t* tag,
uint8_t* plaintext);
// 生成随机IV
int generate_random_iv(uint8_t* iv, size_t iv_len);
int pdu_encrypt(const uint8_t* pdu, size_t pdu_len, uint8_t* encrypted_pdu);
int pdu_decrypt(const uint8_t* encrypted_pdu, size_t encrypted_len, uint8_t* decrypted_pdu);
void aes_gcm_ctrl(unsigned char flag);
#endif
aes_gcm_128.c
#include "aes_gcm_128.h"
#include <stdio.h>
#include <string.h>
// 测试密钥
static uint8_t test_key[AES_128_KEY_SIZE] = {
0x2b, 0x3e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0x57, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};
//是否使用加密, 1使用, 0不使用
static uint8_t encryption_enabled = 1;
int generate_random_iv(uint8_t* iv, size_t iv_len) {
if (iv == NULL || iv_len != GCM_IV_SIZE) {
return 0;
}
return RAND_bytes(iv, iv_len) == 1;
}
int aes_gcm_encrypt(const uint8_t* plaintext, size_t plaintext_len,
const uint8_t* key, const uint8_t* iv,
uint8_t* ciphertext, uint8_t* tag) {
EVP_CIPHER_CTX* ctx;
int len;
int ciphertext_len;
// 创建并初始化加密上下文
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
return 0;
}
// 初始化加密操作
if (EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, key, iv) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
// 设置IV长度
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, GCM_IV_SIZE, NULL) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
// 提供明文数据
if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
ciphertext_len = len;
// 完成加密过程
if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
ciphertext_len += len;
// 获取认证标签
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, GCM_TAG_SIZE, tag) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
int aes_gcm_decrypt(const uint8_t* ciphertext, size_t ciphertext_len,
const uint8_t* key, const uint8_t* iv, const uint8_t* tag,
uint8_t* plaintext) {
EVP_CIPHER_CTX* ctx;
int len = 0;
int plaintext_len = 0;
int ret = 0;
// 创建并初始化解密上下文
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL) {
return 0;
}
// 初始化解密操作
if (EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, key, iv) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
// 设置IV长度
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, GCM_IV_SIZE, NULL) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
// 设置认证标签
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, GCM_TAG_SIZE, (void*)tag) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
// 提供密文数据
if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) != 1) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
plaintext_len = len;
// 完成解密过程
ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
plaintext_len += len;
EVP_CIPHER_CTX_free(ctx);
if (ret > 0) {
return plaintext_len;
}
else {
return 0;
}
}
// AES-GCM加密
int pdu_encrypt(const uint8_t* pdu, size_t pdu_len, uint8_t* encrypted_pdu)
{
uint8_t tag[GCM_TAG_SIZE];
uint8_t iv[GCM_IV_SIZE];
uint8_t tmp[256];
int ciphertext_len = 0;
if (!encryption_enabled) {
memcpy(encrypted_pdu, pdu, pdu_len);
return pdu_len;
}
if (!generate_random_iv(iv, GCM_IV_SIZE)) {
return 0;
}
// 加密PDU数据
ciphertext_len = aes_gcm_encrypt(pdu, pdu_len, test_key, iv, tmp, tag);
if (ciphertext_len == 0) {
return 0;
}
// 将IV复制到加密数据的前面
memcpy(encrypted_pdu, iv, GCM_IV_SIZE);
memcpy(encrypted_pdu + GCM_IV_SIZE, tmp, ciphertext_len);
memcpy(encrypted_pdu + GCM_IV_SIZE + ciphertext_len, tag, GCM_TAG_SIZE);
return ciphertext_len + GCM_IV_SIZE + GCM_TAG_SIZE;
}
// PDU AES-GCM解密
int pdu_decrypt(const uint8_t* encrypted_pdu, size_t encrypted_len, uint8_t* decrypted_pdu)
{
const uint8_t* iv;
const uint8_t* ciphertext;
size_t ciphertext_len = 0;
const uint8_t* tag;
int plaintext_len = 0;
if (!encryption_enabled) {
memcpy(decrypted_pdu, encrypted_pdu, encrypted_len);
return encrypted_len;
}
// 从加密数据中提取IV
iv = encrypted_pdu;
ciphertext = encrypted_pdu + GCM_IV_SIZE;
ciphertext_len = encrypted_len - GCM_IV_SIZE - GCM_TAG_SIZE;
// 提取认证标签(位于密文之后)
tag = ciphertext + ciphertext_len;
if (ciphertext_len < 0) {
return -1;
}
// 解密数据
plaintext_len = aes_gcm_decrypt(ciphertext, ciphertext_len, test_key, iv, tag, decrypted_pdu);
return plaintext_len;
}
//控制是否加密
void aes_gcm_ctrl(unsigned char flag)
{
if (flag == 0) {
encryption_enabled = 0;
}
else {
encryption_enabled = 1;
}
}