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 解密后的明文
如果密文被修改, 就不可能解密成功

笔记

/*!
* \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

GitHub 加速计划 / ope / openssl
20
1
下载
传输层安全性/安全套接层及其加密库
最近提交(Master分支:3 个月前 )
b85e6f53 Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/26250) 7 天前
3974058a Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Neil Horman <nhorman@openssl.org> (Merged from https://github.com/openssl/openssl/pull/26157) 7 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐