openssl3.2 - exp - 用base64后的字符串作为配置项的值

文章目录

openssl3.2 - exp - 用base64后的字符串作为配置项的值

概述

今天解析自己封装的内存型的openssl 配置数据,发现openssl并不支持配置项的值为base64之后的直接字符串。

笔记

配置项的值长度有限制

配置项的值(字符串)的长度好像有512字节的限制,具体没实验。看源码能看出来。

c 复制代码
bool CMyOsslConfig::updateConfig()
{
	bool b_rc = false;
	int i_rc = 0;
	long lineSn = 0;
	int len1 = 0;
	int len2 = 0;

	do {
		if ((NULL == m_pMyOsslMem) || (NULL == m_conf))
		{
			break;
		}

		// BIO_seek(m_bio, 0);
		// len1 = this->bio_get_length(m_bio);
		assert(NULL != m_pMyOsslMem->get_bio());
		i_rc = NCONF_load_bio(m_conf, m_pMyOsslMem->get_bio(), &lineSn); // !
		// len2 = this->bio_get_length(m_bio);
		// m_bio有东西有数据长度, 但是经过NCONF_load_bio()后, m_conf里面有东西了, 但是m_bio的东西没了
		// 所以, 经过NCONF_load_bio后(), 就得当m_bio是空的来处理.
		// 如果是向m_bio中写东西, 然后写配置.
		// 如果要更新m_bio中的配置内容, 就必须重新写.

		if (i_rc <= 0)
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

跟进NCONF_load_bio(), 可以看到openssl读一行开的缓冲区为512字节。

c 复制代码
#define CONFBUFSIZE     512

虽然配置项的值一般没有那么长。不过如果要存一段很大的数据转成的base64值,那么就可能遇到问题。

解决的方法:自己做个多buffer的封装类(C++ - 多个buffer合并成一个buffer的管理类),比较长的信息,自己来管理。配置项值比较短的值,用openssl配置函数来读取。

openssl的配置文件(可以自己封装为内存操作的函数),并没有写入配置的API. 只有读取配置项的API.

如果需要用程序来写openssl的配置文件,可以自己封装一个。写入的格式和普通的ini格式一样(如果简单的用)。

配置项的值不能是base64之后的直接值,需要处理之后才行。

原因 :base64之后的值,每隔64个字符,有一个'\n', 没有了这个'\n', 用openssl的unbase64就不会成功。

折中的方法 :

在base64之后,写入配置项之前,处理一下,将'\n'去掉后,再写入openssl配置文件。

在读取配置项值之后(自己知道是base64的字符串),再处理一下,每64个字符加一个'\n'(如果不是整除64个字符,bas64的尾巴上也要加'\n'), 然后再调用openssl的unbase64的API, 就能执行成功。

openssl配置项的值并不是所有可见字符都可以

有'\n'不行,openssl读取时,是整行(以回车'\n'为结尾)读取的,导致opensslbase64之后的值被截断(如果base64之后的值长度>64),导致unbase64不成功.

有'\'不行,e.g. 一个路径值("d:\my_tmp\xx.dat"), 用openssl配置接口读取出来时,'\'字符消失了, 变为了"d:my_tmpxx.dat",这就乱套了。

如果正好有\d这样的字符,读出的配置项的值直接乱码了。

如果配置项的值中有上述字符,都要base64之后才能正常读取。

例子

将base64写入openssl配置文件(去掉'\n'后,再base64, 然后写入配置项的值)

c 复制代码
	// 
	obj.add_config_section("PE");

	//
	ptszBuf = (TCHAR*)m_PeFileCheck.getFilePathName();
	// 需要base64,否则'\'都没了,还有乱码
	strTmp = strOpt.my_W2A(ptszBuf);
	base64.base64(true, (UCHAR*)strTmp.data(), (int)strTmp.size(), pOsslBufTmp, len_OsslBufTmp);
	// base64之后,每65个字符之后有一个\n, 不能移除,base64解码时,如果不是每64个字符后面带一个\n, 会unbase64失败
	strTmp = base64.remove_char_0a(pOsslBufTmp, len_OsslBufTmp);
	
	obj.add_config_item_after_section("getFilePathName", (const char*)strTmp.data());
	MY_OPENSSL_FREE(pOsslBufTmp);

用openssl配置文件的一方,先读出配置项的值,加上'\n'(每64个字符), 再unbase64.

c 复制代码
		pszKeyValue = _ossl_cfg.get_conf_item_value("PE", "getFilePathName");
		if (NULL == pszKeyValue)
		{
			break;
		}

		strTmp = base64.add_char_0a((UCHAR*)pszKeyValue, (int)strlen(pszKeyValue));
		b = base64.base64(false, (UCHAR*)strTmp.data(), (int)strTmp.size(), pOsslBufTmp, lenOsslBufTmp);
		if (!b)
		{
			break;
		}

现在用的base64的类

cipher_base64.h

c 复制代码
//! \file cipher_base64.h

#ifndef __CIPHER_BASE64_H__
#define __CIPHER_BASE64_H__

class CBase64
{
public:
	CBase64();
	virtual ~CBase64();

	bool base64(bool isEncode, UCHAR* pucBufIn, int lenBufIn, UCHAR*& pucBufOut, int& lenBufOut);
	std::string remove_char_0a(UCHAR* pucBufIn, int lenBufIn);
	std::string add_char_0a(UCHAR* pucBufIn, int lenBufIn);
};

#endif // #ifndef __CIPHER_BASE64_H__

cipher_base64.cpp

c 复制代码
//! \file cipher_base64.cpp

#include "pch.h"
#include "cipher_base64.h"

#include "openssl/bio.h"
#include "openssl/evp.h"

#include "memOpt/my_debug_new_define.h"

CBase64::CBase64()
{

}

CBase64::~CBase64()
{

}

std::string CBase64::remove_char_0a(UCHAR* pucBufIn, int lenBufIn)
{
	std::string str_rc;
	int i = 0;

	do {
		if ((NULL == pucBufIn) || (lenBufIn <= 0))
		{
			break;
		}

		for (i = 0; i < lenBufIn; i++)
		{
			if (pucBufIn[i] != '\n')
			{
				str_rc += pucBufIn[i];
			}
		}
	} while (false);

	return str_rc;
}

std::string CBase64::add_char_0a(UCHAR* pucBufIn, int lenBufIn)
{
	std::string str_rc;
	int cnt = 0;
	int i = 0;

	// openssl每一行(64个字符一行,最后一行除外)后面都有一个'\n', 才能正确解码
	do {
		if ((NULL == pucBufIn) || (lenBufIn <= 0))
		{
			break;
		}

		for (i = 0; i < lenBufIn; i++)
		{
			str_rc += pucBufIn[i];
			if (++cnt == 64)
			{
				cnt = 0;
				str_rc += '\n';
			}
		}
	} while (false);

	if (cnt > 0)
	{
		str_rc += '\n';
	}
	
	return str_rc;
}

bool CBase64::base64(bool isEncode, UCHAR* pucBufIn, int lenBufIn, UCHAR*& pucBufOut, int& lenBufOut)
{
	bool b_rc = false;
	BIO* bio_container = NULL;
	BIO* bio_to_base64 = NULL;

	BIO* bio_header = NULL; // BIO链头
	BIO* bio_tail = NULL; // BIO链尾

	BIO* bio_to_write = NULL; // 将数据写入的BIO指针
	BIO* bio_read_from = NULL; // 将数据读出的BIO指针

	size_t sz_wt = 0;
	size_t sz_rd = 0;
	int i_rc = 0;
	int len = 0;

	do {
		pucBufOut = NULL;
		lenBufOut = 0;
		if ((NULL == pucBufIn) || (lenBufIn <= 0))
		{
			break;
		}

		bio_container = BIO_new(BIO_s_mem());
		if (NULL == bio_container)
		{
			break;
		}

		bio_to_base64 = BIO_new(BIO_f_base64());
		if (NULL == bio_to_base64)
		{
			break;
		}

		bio_header = BIO_push(bio_to_base64, bio_container);
		bio_tail = bio_container;

		if (isEncode)
		{
			bio_to_write = bio_header;
			bio_read_from = bio_tail;
		}
		else {
			// ! base64解码时, 是从链尾写入, 从链头读取
			bio_to_write = bio_tail;
			bio_read_from = bio_header;
		}

		i_rc = BIO_write_ex(bio_to_write, pucBufIn, lenBufIn, &sz_wt);
		if ((1 != i_rc) || (lenBufIn != sz_wt))
		{
			break;
		}

		BIO_flush(bio_to_write); // 数据写完后, 必须对写入的BIO执行 BIO_flush.

		// 必须从bio_read_from读取处理完的数据长度, 才是处理之后的数据长度
		len = BIO_pending(bio_read_from); // 必须BIO_flush()之后, 才能读取到BIO内可以读出的数据长度. 否则读出的长度是0
		// 当解码时, 得到的处理完的长度还是没解码之前的长度, 不过不影响
		// 拿这个长度开buffer, 实际数据处理完的长度按照从bio_read_from()中累计出的数据长度为准

		// 将处理过的数据从bio_header中读出来
		pucBufOut = (UCHAR*)OPENSSL_malloc(len + 1); // 再多加1个字节的空间, 便于观察得到的可见字符串
		if (NULL == pucBufOut)
		{
			break;
		}

		pucBufOut[len] = '\0';

		do {
			// 不能从bio_header读取, 因为读取后, 还是原来的数据长度
			i_rc = BIO_read_ex(bio_read_from, pucBufOut + lenBufOut, len - lenBufOut, &sz_rd);
			if (i_rc <= 0)
			{
				// 多次读, 直到读空了, 不算错
				break;
			}

			lenBufOut += (int)sz_rd;
		} while (true);

		if (NULL != pucBufOut)
		{
			pucBufOut[lenBufOut] = 0x00;
		}

		b_rc = true;
	} while (false);

	if (NULL != bio_header)
	{
		BIO_free_all(bio_header);
		bio_header = NULL;
	}

	return b_rc;
}

现在用的openssl配置文件读写类

CMyOsslConfig.h

c 复制代码
//! \file CMyOsslConfig.h

#ifndef __CMYOSSLCONFIG_H__
#define __CMYOSSLCONFIG_H__

#include <openssl/bio.h>
#include <openssl/conf.h> // for CONF
#include "CMyOsslMem.h"

class CMyOsslConfig
{
public:
	CMyOsslConfig();
	virtual ~CMyOsslConfig();

	bool init();
	void uninit();
	bool add_config_section(const char* pszIn);
	bool add_config_item_after_section(const char* pszItemName, const char* pszItemContent);
	bool append_to_bio(uint8_t* pBuf, int lenBuf);
	bool updateConfig();
	char* get_conf_item_value(const char* group, const char* name);

	CMyOsslMem* getMem() { return m_pMyOsslMem; }

private:
	CMyOsslMem* m_pMyOsslMem;
	CONF* m_conf;
};

#endif // #ifndef __CMYOSSLCONFIG_H__

CMyOsslConfig.cpp

c 复制代码
//! \file CMyOsslConfig.cpp

#include "pch.h"

#include "CMyOsslConfig.h"
#include <string.h>
#include <openssl/err.h>
#include <cassert>
#include "CMyOsslMem.h"

#include "memOpt/my_debug_new_define.h"

CMyOsslConfig::CMyOsslConfig()
	:m_pMyOsslMem(NULL),
	m_conf(NULL)
{

}

CMyOsslConfig::~CMyOsslConfig()
{
	// 必须在程序退出前,主动调用uninit(), 否则在mem_unhook()时,就会报mem leak的错
}

void CMyOsslConfig::uninit()
{
	if (NULL != m_pMyOsslMem)
	{
		m_pMyOsslMem->uninit();
		delete m_pMyOsslMem;
		m_pMyOsslMem = NULL;
	}

	if (NULL != m_conf)
	{
		NCONF_free(m_conf);
		m_conf = NULL;
	}
}

bool CMyOsslConfig::init()
{
	bool b_rc = false;

	do {
		if (NULL == m_pMyOsslMem)
		{
			m_pMyOsslMem = new CMyOsslMem();
			assert(NULL != m_pMyOsslMem);
			if (NULL == m_pMyOsslMem)
			{
				break;
			}

			m_pMyOsslMem->init();
		}

		if (NULL == m_conf)
		{
			m_conf = NCONF_new_ex(OSSL_LIB_CTX_get0_global_default(), NULL);
			if (NULL == m_conf)
			{
				break;
			}
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslConfig::add_config_section(const char* pszIn)
{
	bool b_rc = false;
	char szBuf[1024];
	int len = 0;

	do {
		if (NULL == pszIn)
		{
			break;
		}

		len = (int)strlen(pszIn);
		if (len > (sizeof(szBuf) - 0x10))
		{
			break;
		}

		sprintf(szBuf, "[ %s ]\n", pszIn);
		assert(NULL != m_pMyOsslMem);
		if (!m_pMyOsslMem->append_to_bio((uint8_t*)szBuf, (int)strlen(szBuf)))
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslConfig::add_config_item_after_section(const char* pszItemName, const char* pszItemContent)
{
	bool b_rc = false;
	char szBuf[1024];
	int len = 0;

	do {
		if ((NULL == pszItemName) || (NULL == pszItemContent))
		{
			break;
		}

		len = (int)(strlen(pszItemName) + strlen(pszItemContent));
		if (len > (sizeof(szBuf) - 0x10))
		{
			break;
		}

		sprintf(szBuf, "%s = %s\n", pszItemName, pszItemContent);

		if (NULL == m_pMyOsslMem)
		{
			break;
		}

		assert(NULL != m_pMyOsslMem);
		if (!m_pMyOsslMem->append_to_bio((uint8_t*)szBuf, (int)strlen(szBuf)))
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslConfig::append_to_bio(uint8_t* pBuf, int lenBuf)
{
	assert(NULL != m_pMyOsslMem);
	return m_pMyOsslMem->append_to_bio(pBuf, lenBuf);
}

bool CMyOsslConfig::updateConfig()
{
	bool b_rc = false;
	int i_rc = 0;
	long lineSn = 0;
	int len1 = 0;
	int len2 = 0;

	do {
		if ((NULL == m_pMyOsslMem) || (NULL == m_conf))
		{
			break;
		}

		// BIO_seek(m_bio, 0);
		// len1 = this->bio_get_length(m_bio);
		assert(NULL != m_pMyOsslMem->get_bio());
		i_rc = NCONF_load_bio(m_conf, m_pMyOsslMem->get_bio(), &lineSn);
		// len2 = this->bio_get_length(m_bio);
		// m_bio有东西有数据长度, 但是经过NCONF_load_bio()后, m_conf里面有东西了, 但是m_bio的东西没了
		// 所以, 经过NCONF_load_bio后(), 就得当m_bio是空的来处理.
		// 如果是向m_bio中写东西, 然后写配置.
		// 如果要更新m_bio中的配置内容, 就必须重新写.

		if (i_rc <= 0)
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

char* CMyOsslConfig::get_conf_item_value(const char* group, const char* name)
{
	char* res = NULL;
	
	int i_rc = 0;

	do {
		if (NULL == m_conf)
		{
			break;
		}

		res = NCONF_get_string(m_conf, group, name);
		if (NULL == res)
		{
			ERR_pop_to_mark();
		}
		else {
			ERR_clear_last_mark();
		}
	} while (false);

	return res;
}

CMyOsslMem.h

c 复制代码
//! \file CMyOsslMem.h

#ifndef __C_MY_OSSL_MEM_H__
#define __C_MY_OSSL_MEM_H__

#include <openssl/bio.h>
#include <openssl/conf.h> // for CONF

class CMyOsslMem
{
public:
	CMyOsslMem();
	virtual ~CMyOsslMem();

	bool init();
	void uninit();

	BIO* get_bio();

	bool append_to_bio(uint8_t* pBuf, int lenBuf);
	bool bio_to_buf(BIO* bio, uint8_t*& pBuf, int& lenBuf); // 执行之后,东西还在. 需要调用者释放pBuf

	bool to_file(const TCHAR* psz_file_pathname); // for test only
	bool get_bio_buffer(uint8_t*& pBuf, int& lenBuf); // 需要自己释放(OPENSSL_free)

	size_t get_length();
	size_t bio_get_length(BIO* bio);

	unsigned char* get_bio_data_tail(); // only for test

private:
	BIO* m_bio;

	// only for test
	unsigned char* m_bio_data;
	int m_bio_len;
};

#endif // #ifndef __C_MY_OSSL_MEM_H__

CMyOsslMem.cpp

c 复制代码
//! \file CMyOsslMem.cpp

#include "pch.h"

#include "CMyOsslMem.h"
#include <string.h>
#include <openssl/err.h>
#include <cassert>

#include "memOpt/my_debug_new_define.h"

CMyOsslMem::CMyOsslMem()
	:m_bio(NULL),
	m_bio_len(0),
	m_bio_data(NULL)
{

}

CMyOsslMem::~CMyOsslMem()
{
	// 必须在程序退出前,主动调用uninit(), 否则在mem_unhook()时,就会报mem leak的错
}

void CMyOsslMem::uninit()
{
	if (NULL != m_bio)
	{
		BIO_free(m_bio);
		m_bio = NULL;
	}
}

bool CMyOsslMem::init()
{
	bool b_rc = false;

	do {
		if (NULL == m_bio)
		{
			m_bio = BIO_new(BIO_s_mem());
			if (NULL == m_bio)
			{
				assert(false);
				break;
			}
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslMem::append_to_bio(uint8_t* pBuf, int lenBuf)
{
	bool b_rc = false;
	int len = 0;
	size_t szLen = 0;
	int bio_len_before = 0;
	int bio_len_after = 0;

	unsigned char* pDebug = NULL;
	int lenDebug = 0;

	do {
		if ((NULL == pBuf) || (lenBuf <= 0))
		{
			break;
		}

		if (NULL == m_bio)
		{
			assert(false);
			break;
		}

		// szLen = bio_get_length(m_bio);
		// BIO_seek(m_bio, szLen);
		bio_len_before = (int)bio_get_length(m_bio);
		len = BIO_write(m_bio, pBuf, lenBuf);
		bio_len_after = (int)bio_get_length(m_bio);
		if (len != lenBuf)
		{
			break;
		}

		if (len != (bio_len_after - bio_len_before))
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;

}

BIO* CMyOsslMem::get_bio()
{
	if (NULL != m_bio)
	{
		// BIO_seek(m_bio, 0);
	}

	return m_bio;
}

size_t CMyOsslMem::get_length()
{
	return bio_get_length(this->get_bio());
}

size_t CMyOsslMem::bio_get_length(BIO* bio)
{
	size_t bio_length = 0;

	do {
		if (NULL == bio)
		{
			break;
		}

		// BIO_seek(bio, 0);
		bio_length = BIO_ctrl_pending(bio);
	} while (false);

	return bio_length;
}

unsigned char* CMyOsslMem::get_bio_data_tail()
{
	unsigned char* p_rc = NULL;

	do {
		if (NULL == m_bio)
		{
			assert(false);
			break;
		}

		// 取出的指针是bio内部的buffer地址,不用释放
		m_bio_len = BIO_get_mem_data(m_bio, &m_bio_data);
		p_rc = m_bio_data + m_bio_len;
	} while (false);

	return p_rc;
}

bool CMyOsslMem::to_file(const TCHAR* psz_file_pathname)
{
	bool b_rc = false;
	FILE* pf = NULL;
	uint8_t* pBuf = NULL;
	int len = 0;
	size_t sz_rc = 0;

	do {
		if (NULL == psz_file_pathname)
		{
			break;
		}

		pf = _tfopen(psz_file_pathname, TEXT("w+b"));
		if (NULL == pf)
		{
			break;
		}

		if (!get_bio_buffer(pBuf, len))
		{
			break;
		}

		if ((NULL == pBuf) || (len <= 0))
		{
			break;
		}

		sz_rc = fwrite(pBuf, sizeof(char), len, pf);
		assert(sz_rc == len);

		b_rc = true;
	} while (false);

	if (NULL != pf)
	{
		fclose(pf);
		pf = NULL;
	}
	
	if (NULL != pBuf)
	{
		OPENSSL_free(pBuf);
		pBuf = NULL;
	}

	return b_rc;
}

bool CMyOsslMem::get_bio_buffer(uint8_t*& pBuf, int& lenBuf)
{
	bool b_rc = false;

	do {
		if (!bio_to_buf(get_bio(), pBuf, lenBuf))
		{
			break;
		}

		if ((NULL == pBuf) || (lenBuf <= 0))
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslMem::bio_to_buf(BIO* bio, uint8_t*& pBuf, int& lenBuf)
{
	bool b_rc = false;
	int i_rc = 0;

	do {
		if (NULL == bio)
		{
			break;
		}

		lenBuf = (int)bio_get_length(bio);
		pBuf = (uint8_t*)OPENSSL_malloc(lenBuf + 1);
		if (NULL == pBuf)
		{
			break;
		}

		pBuf[lenBuf] = '\0';
		i_rc = BIO_read(bio, pBuf, lenBuf);
		BIO_seek(bio, 0); // ! 读完了, 将数据读指针恢复.

		b_rc = (i_rc == lenBuf);
	} while (false);

	return b_rc;
}

END

相关推荐
Lazy Dave11 天前
gmssl私钥文件格式
网络安全·ssl·openssl
沉在嵌入式的鱼1 个月前
RK3588移植Openssl库
linux·rk3588·openssl
黑屋里的马1 个月前
ssl相关命令生成证书
服务器·网络·ssl·openssl·gmssl
fangeqin1 个月前
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·交叉编译