openssl3.2 - exp - AES-256-GCM

文章目录

openssl3.2 - exp - AES-256-GCM

概述

工程中要用到对称加密, 没得选, 要用AES256.

在openssl3.2中, AES256加解密的种类有好多种.

查了资料, 用AE-S256-GCM不错. 如果密文被修改, 就无法解密成功. 不用再另外传HASH给解密一方(让对方自己算是否密文被修改).

AES-256-GCM的明文可以为任意长度, 不需要进行填充对齐(padding).

AES-256-GCM加密入参

PT - 明文

KEY

IV

AAD - 认证数据(双方已经的确定数据), 我这里用的32字节, 等用到的时候, 再试试, 是否可以为不规则长度的数据, 是否有长度限制

AES-256-GCM加密出参

RC - 1为TRUE, 0为失败

CT - 密文

TAG - 16字节, 解密时要用

AES-256-GCM解密入参

CT - 密文

KEY

IV

AAD - 同加密时是一个东西

TAG - 加密出参返回的16个字节的TAG

AES-256-GCM解密出参

CT-PT 解密后的明文
如果密文被修改, 就不可能解密成功

笔记

c 复制代码
/*!
* \file main.cpp
* \note openssl3.2 - exp - AES-256-GCM
* AES-256-GCM可以知道密文是否被修改, 而普通的AES-256直接无脑解密
*/

//! \note assert()只在debug版下生效
//! 如果是release版, 相当于没有assert()括号中的那些内容
//! 所以, 不要将逻辑代码放到断言中. 如果是要加断言, 也要判断逻辑代码执行完后的标记值.

#include "my_openSSL_lib.h"
#include <openssl/crypto.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/core_names.h>

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "CMemHookRec.h"

void my_openssl_app();

// ct means 密文输出
// pt means 明文
// add means 附加认证数据
bool enc_aes_256_gcm(OUT UCHAR* ct, OUT int& len_ct, UCHAR* pt, int len_pt, UCHAR* key, int len_key, UCHAR* iv, int len_iv, UCHAR* aad, int len_aad, OUT UCHAR* tag, IN int len_tag);
bool dec_aes_256_gcm(IN UCHAR* ct, IN int len_ct, OUT UCHAR* pt, OUT int& len_pt, UCHAR* key, int len_key, UCHAR* iv, int len_iv, UCHAR* aad, int len_aad, IN UCHAR* tag, IN int len_tag);

int main(int argc, char** argv)
{
	setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞
	mem_hook();

	my_openssl_app();

	mem_unhook();

	return 0;
}

char int2char(int i)
{
	char c_rc = '\0';

	i = i % 0x10;
	if ((i >= 0) && (i <= 9))
	{
		c_rc = '0' + i;
	}
	else if ((i >= 0x0A) && (i <= 0x0F))
	{
		c_rc = 'A' + (i - 0x0A);
	}
	else {
		assert(false);
	}

	return c_rc;
}

// #define SIM_CT_MODIFY // 定义此宏, 模拟密文被修改后再解密的错误

void my_openssl_app()
{	
	// 算法参数
	UCHAR gcm_key[32];
	size_t gcm_keylen = sizeof(gcm_key);

	UCHAR gcm_iv[32];
	size_t gcm_ivlen = sizeof(gcm_iv);

	// 附加认证数据(选择加解密双方都知道的确定数据就行)
	UCHAR gcm_aad[32];
	int gcm_addlen = sizeof(gcm_aad);

	int i_rc = 0;
	char gcm_pt[4 * 1024 - 3]; // GCM明文
	int i = 0;

	char gcm_ct[4 * 1024]; // CGM密文
	int len_ct = sizeof(gcm_ct);

	char gcm_ct_pt[4 * 1024]; // CGM密文解密后的明文
	int len_ct_pt = sizeof(gcm_ct_pt);

	// 加密时, 返回密文和TAG
	// 解密时, 要给加密返回的TAG
	UCHAR gcm_tag[16]; // 单步openssl实现, tag最多16个字节, 否则报错

	bool b_rc = false;

	do {
		// 准备GCM明文
		for (i = 0; i < sizeof(gcm_pt); i++)
		{
			gcm_pt[i] = int2char(i);
		}

		// 准备gcm key
		i_rc = RAND_bytes(gcm_key, sizeof(gcm_key));
		if (1 != i_rc)
		{
			assert(false);
			break;
		}

		// 准备gcm iv
		i_rc = RAND_bytes(gcm_iv, sizeof(gcm_iv));
		if (1 != i_rc)
		{
			assert(false);
			break;
		}

		// 初始化aad(实际应用中, 这个gcm_aad是双方都知道的确定数据)
		i_rc = RAND_bytes(gcm_aad, sizeof(gcm_aad));
		if (1 != i_rc)
		{
			assert(false);
			break;
		}
		
		// 加解密时的入参(key, iv, aad)都是一样的

		// 加密后, 会返回密文和TAG, tag最多16个字节, tag解密时要用
		if (!enc_aes_256_gcm((UCHAR*)gcm_ct, len_ct, (UCHAR*)gcm_pt, sizeof(gcm_pt), gcm_key, sizeof(gcm_key), gcm_iv, sizeof(gcm_iv), gcm_aad, sizeof(gcm_aad), gcm_tag, sizeof(gcm_tag)))
		{
			assert(false);
			break;
		}

		// 解密时, 要给出密文和tag, 返回解密后的明文
		if (!dec_aes_256_gcm((UCHAR*)gcm_ct, len_ct, (UCHAR*)gcm_ct_pt, len_ct_pt, gcm_key, sizeof(gcm_key), gcm_iv, sizeof(gcm_iv), gcm_aad, sizeof(gcm_aad), gcm_tag, sizeof(gcm_tag)))
		{
			assert(false);
			break;
		}

		// 比较原始明文和解密后的明文是否一致
		if (len_ct_pt != sizeof(gcm_pt))
		{
			assert(false);
			break;
		}

		i_rc = memcmp(gcm_pt, gcm_ct_pt, sizeof(gcm_pt));
		if (0 != i_rc)
		{
			assert(false);
			break;
		}

#ifdef SIM_CT_MODIFY
		// 模拟密文被修改的情况, 这时用普通的AES256解密是验证不出来密文是否被修改(都是buffer), 而AES-256-GCM可以
		// 解密时, 要给出密文和tag, 返回解密后的明文
		gcm_ct[0] = gcm_ct[0] + 1;

		// 在dec_aes_256_gcm()中的 EVP_DecryptFinal_ex()处, 就执行失败
		if (!dec_aes_256_gcm((UCHAR*)gcm_ct, len_ct, (UCHAR*)gcm_ct_pt, len_ct_pt, gcm_key, sizeof(gcm_key), gcm_iv, sizeof(gcm_iv), gcm_aad, sizeof(gcm_aad), gcm_tag, sizeof(gcm_tag)))
		{
			assert(false);
			break;
		}

		// 比较原始明文和解密后的明文是否一致
		if (len_ct_pt != sizeof(gcm_pt))
		{
			assert(false);
			break;
		}

		i_rc = memcmp(gcm_pt, gcm_ct_pt, sizeof(gcm_pt));
		if (0 != i_rc)
		{
			assert(false);
			break;
		}
#endif // #ifdef SIM_CT_MODIFY

		b_rc = true;
	} while (false);

	assert(true == b_rc);
}

bool enc_aes_256_gcm(OUT UCHAR* ct, OUT int& len_ct, UCHAR* pt, int len_pt, UCHAR* key, int len_key, UCHAR* iv, int len_iv, UCHAR* aad, int len_aad, OUT UCHAR* tag, IN int len_tag)
{
	// openssl库上下文和属性
	OSSL_LIB_CTX* libctx = NULL;
	const char* propq = NULL;

	// 算法上下文和算法指针
	EVP_CIPHER_CTX* ctx = NULL;
	EVP_CIPHER* cipher = NULL;
	OSSL_PARAM gcm_params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };

	size_t gcm_ivlen = 0;

	bool b_rc = false;
	int i_rc = 0;

	int tmplen = 0;

	do {
		if ((NULL == ct) || (len_ct <= 0) || (NULL == pt) || (len_pt <= 0) || (len_ct < len_pt))
		{
			assert(false);
			break;
		}

		if ((NULL == key) || (len_key <= 0) || (NULL == iv) || (len_iv <= 0))
		{
			assert(false);
			break;
		}

		ctx = EVP_CIPHER_CTX_new();
		assert(NULL != ctx);
		if (NULL == ctx)
		{
			break;
		}

		cipher = EVP_CIPHER_fetch(libctx, "AES-256-GCM", propq);
		assert(NULL != cipher);
		if (NULL == cipher)
		{
			break;
		}

		gcm_ivlen = len_iv;
		gcm_params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, &gcm_ivlen);

		i_rc = EVP_EncryptInit_ex2(ctx, cipher, key, iv, gcm_params);
		assert(1 == i_rc);
		if (1 != i_rc)
		{
			break;
		}

		// set aad to ctx
		tmplen = 0;
		i_rc = EVP_EncryptUpdate(ctx, NULL, &tmplen, aad, len_aad);
		assert(1 == i_rc);
		if (1 != i_rc)
		{
			break;
		}

		assert(tmplen == len_aad);
		if (tmplen != len_aad)
		{
			break;
		}

		assert(len_pt <= len_ct);
		if (len_pt > len_ct)
		{
			break;
		}

		i_rc = EVP_EncryptUpdate(ctx, ct, &len_ct, pt, len_pt);
		assert(1 == i_rc);
		if (1 != i_rc)
		{
			break;
		}

		assert(len_ct == len_pt);
		if (len_ct != len_pt)
		{
			break;
		}

		// get no output's enc text
		i_rc = EVP_EncryptFinal_ex(ctx, ct + len_ct, &tmplen);
		assert(1 == i_rc);
		if (tmplen > 0)
		{
			len_ct += tmplen;
		}

		/* Get tag */
		// aes-256-gcm加密完, 除了输出密文, 还输出tag.
		// 输出的密文和tag给解密用
		gcm_params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, tag, len_tag);
		i_rc = EVP_CIPHER_CTX_get_params(ctx, gcm_params);
		assert(1 == i_rc);
		if (1 != i_rc)
		{
			break;
		}

		b_rc = true;
	} while (false);

	if (NULL != cipher)
	{
		EVP_CIPHER_free(cipher);
	}
	
	if (NULL != ctx)
	{
		EVP_CIPHER_CTX_free(ctx);
	}

	return b_rc;
}

bool dec_aes_256_gcm(IN UCHAR* ct, IN int len_ct, OUT UCHAR* pt, OUT int& len_pt, UCHAR* key, int len_key, UCHAR* iv, int len_iv, UCHAR* aad, int len_aad, IN UCHAR* tag, IN int len_tag)
{
	// openssl库上下文和属性
	OSSL_LIB_CTX* libctx = NULL;
	const char* propq = NULL;

	// 算法上下文和算法指针
	EVP_CIPHER_CTX* ctx = NULL;
	EVP_CIPHER* cipher = NULL;
	OSSL_PARAM gcm_params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };

	size_t gcm_ivlen = 0;

	bool b_rc = false;
	int i_rc = 0;

	int tmplen = 0;

	do {
		if ((NULL == ct) || (len_ct <= 0) || (NULL == pt) || (len_pt <= 0) || (len_ct > len_pt))
		{
			assert(false);
			break;
		}

		if ((NULL == key) || (len_key <= 0) || (NULL == iv) || (len_iv <= 0))
		{
			assert(false);
			break;
		}

		ctx = EVP_CIPHER_CTX_new();
		if (NULL == ctx)
		{
			assert(false);
			break;
		}
		
		cipher = EVP_CIPHER_fetch(libctx, "AES-256-GCM", propq);
		if (NULL == cipher)
		{
			assert(false);
			break;
		}

		gcm_ivlen = len_iv;
		gcm_params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, &gcm_ivlen);

		i_rc = EVP_DecryptInit_ex2(ctx, cipher, key, iv, gcm_params);
		if (1 != i_rc)
		{
			assert(false);
			break;
		}

		// set aad to ctx
		tmplen = 0;
		i_rc = EVP_DecryptUpdate(ctx, NULL, &tmplen, aad, len_aad);
		if ((1 != i_rc) || (tmplen != len_aad))
		{
			assert(false);
			break;
		}

		i_rc = EVP_DecryptUpdate(ctx, pt, &len_pt, ct, len_ct);
		if (1 != i_rc)
		{
			assert(false);
			break;
		}

		/* Set expected tag value. */
		gcm_params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, (void*)tag, len_tag);

		i_rc = EVP_CIPHER_CTX_set_params(ctx, gcm_params);
		if (1 != i_rc)
		{
			assert(false);
			break;
		}

		/* Finalise: note get no output for GCM */
		i_rc = EVP_DecryptFinal_ex(ctx, pt + len_pt, &tmplen);
		if (1 != i_rc)
		{
			// 如果密文被修改, 到这里就有断言
			assert(false);
			break;
		}

		if (tmplen > 0)
		{
			len_pt += tmplen;
		}

		b_rc = true;
	} while (false);

	if (NULL != cipher)
	{
		EVP_CIPHER_free(cipher);
	}

	if (NULL != ctx)
	{
		EVP_CIPHER_CTX_free(ctx);
	}

	return b_rc;
}

END

相关推荐
Lazy Dave15 天前
gmssl私钥文件格式
网络安全·ssl·openssl
沉在嵌入式的鱼1 个月前
RK3588移植Openssl库
linux·rk3588·openssl
黑屋里的马1 个月前
ssl相关命令生成证书
服务器·网络·ssl·openssl·gmssl
fangeqin2 个月前
ubuntu源码安装python3.13遇到Could not build the ssl module!解决方法
linux·python·ubuntu·openssl
API开发2 个月前
苹果芯片macOS安装版Homebrew(亲测) ,一键安装node、python、vscode等,比绿色软件还干净、无污染
vscode·python·docker·nodejs·openssl·brew·homebrew
码农不惑2 个月前
Rust使用tokio(二)HTTPS相关
https·rust·web·openssl
liulilittle2 个月前
通过高级处理器硬件指令集AES-NI实现AES-256-CFB算法并通过OPENSSL加密验证算法正确性。
linux·服务器·c++·算法·安全·加密·openssl
liulilittle2 个月前
OpenSSL 的 AES-NI 支持机制
linux·运维·服务器·算法·加密·openssl·解密
liulilittle2 个月前
通过高级处理器硬件指令集AES-NI实现AES-256-CFB算法。
linux·服务器·c++·算法·安全·加密·openssl
花花少年2 个月前
Ubuntu系统下交叉编译openssl
openssl·交叉编译