文章目录
-
- [openssl3.2 - exp - 用base64后的字符串作为配置项的值](#openssl3.2 - exp - 用base64后的字符串作为配置项的值)
- 概述
- 笔记
- 配置项的值不能是base64之后的直接值,需要处理之后才行。
- openssl配置项的值并不是所有可见字符都可以
- 例子
- 现在用的base64的类
- 现在用的openssl配置文件读写类
- END
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;
}