赞
踩
今天解析自己封装的内存型的openssl 配置数据,发现openssl并不支持配置项的值为base64之后的直接字符串。
配置项的值(字符串)的长度好像有512字节的限制,具体没实验。看源码能看出来。
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字节。
#define CONFBUFSIZE 512
虽然配置项的值一般没有那么长。不过如果要存一段很大的数据转成的base64值,那么就可能遇到问题。
解决的方法:自己做个多buffer的封装类(C++ - 多个buffer合并成一个buffer的管理类),比较长的信息,自己来管理。配置项值比较短的值,用openssl配置函数来读取。
openssl的配置文件(可以自己封装为内存操作的函数),并没有写入配置的API. 只有读取配置项的API.
如果需要用程序来写openssl的配置文件,可以自己封装一个。写入的格式和普通的ini格式一样(如果简单的用)。
原因 :base64之后的值,每隔64个字符,有一个’\n’, 没有了这个’\n’, 用openssl的unbase64就不会成功。
折中的方法 :
在base64之后,写入配置项之前,处理一下,将’\n’去掉后,再写入openssl配置文件。
在读取配置项值之后(自己知道是base64的字符串),再处理一下,每64个字符加一个‘\n’(如果不是整除64个字符,bas64的尾巴上也要加’\n’), 然后再调用openssl的unbase64的API, 就能执行成功。
有’\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, 然后写入配置项的值)
//
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.
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;
}
//! \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__
//! \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; }
//! \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__
//! \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; }
//! \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__
//! \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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。