openssl3.2 - exp - buf to bio

文章目录

openssl3.2 - exp - buf to bio

概述

不想让程序调用openssl API时, 有文件落地的动作.

如果程序有配置文件要用, 也是自己读文件到buffer, 然后转成BIO给openssl的相关有BIO入参的API用.

如果在程序中用数组定义了一些PEM内容的数组, 也可以将数组转成BIO来用.

从openssl测试代码中, 找到了BIO_new_mem_buf(), 可以做这个事情.

那么程序需要的文件输入, 都可以读入buffer, 转成BIO, 后续都用BIO来处理.

笔记

c 复制代码
/*!
* \file main.cpp
* \note buffer to bio
*/

#include "my_openSSL_lib.h"

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

#include <openssl/bio.h>

int test_bio_new_mem_buf(void);

int main(int argc, char** argv)
{
    test_bio_new_mem_buf();
	return 0;
}

int test_bio_new_mem_buf(void)
{
    int ok = 0;
    BIO* bio = NULL;
    BUF_MEM* bufmem = NULL;
    char data[16];
    int i_rc = 0;

    do {
        // BIO_free(bio); // openssl做了处理, 即使入参为NULL, 也不会报错

        // BIO_new_mem_buf是直接将入参的buffer指到内部指针上, 所以入参指针如果是自己new出来的, 就需要自己释放

        // 如果是文件输入, 可以自己读文件到buffer中, 然后就用BIO来操作.
        bio = BIO_new_mem_buf("Hello World\n", 12);
        if (NULL == bio)
        {
            break; 
        }
        
		i_rc = BIO_get_mem_ptr(bio, &bufmem);
        if (i_rc <= 0)
        {
            break;
        }

        memset(data, 0, sizeof(data));
        if (5 != BIO_read(bio, data, 5)) 
        { 
            break; 
        }

        if (0 != strncmp(data, "Hello", 5))
        {
            break;
        }

        // b->flags & BIO_FLAGS_MEM_RDONLY
        // bio默认是只读的, 是写不进去的.
        i_rc = BIO_write(bio, "test", 4);
        assert(i_rc <= 0);

        memset(data, 0, sizeof(data));
        i_rc = BIO_read(bio, data, 16); // 连续调用BIO_read时, 是继续往后读, 读一点, 内容就少一点
        assert(7 == i_rc); // 返回值是读了多少个字节

        if (0 != strncmp(data, " World\n", 7))
        {
            break;
        }

        i_rc = BIO_reset(bio); // 只读的bio可以恢复到刚创建时的状态, 可以从头开始读.
        assert(i_rc > 0);

        i_rc = BIO_read(bio, data, 16);
        assert(12 == i_rc);

        if (0 != strncmp(data, "Hello World\n", 12))
        {
            break;
        }

        ok = 1;
    } while (false);

    if (NULL != bio)
    {
        BIO_free_all(bio);
        bio = NULL;
    }
    
    return ok;
}

bio_get_length

openssl 并不提供如何得到BIO对象内容的size.

因为很多BIO是不知道有多长的(e.g. bio for ssl)

但是对于由buffer转成BIO之后, 其实BIO内的数据长度是确定的.

查了资料, 可以用 BIO_ctrl_pending()来取长度, 但是之前要先调用一下BIO_rest().

封装了一个函数bio_get_length(), 好使.

调用端代码

c 复制代码
        // 如果是文件输入, 可以自己读文件到buffer中, 然后就用BIO来操作.
        bio = BIO_new_mem_buf("Hello World\n", 12);
        if (NULL == bio)
        {
            break; 
        }

        bio_length = bio_get_length(bio);
        assert(bio_length == 12);

函数实现

c 复制代码
size_t bio_get_length(BIO* bio)
{
	size_t bio_length = 0;

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

		BIO_reset(bio);
		bio_length = BIO_ctrl_pending(bio);
	} while (false);

	return bio_length;
}

bio_to_buffer

如果想对一个已经包含了内容的BIO中的内容, 转为buffer. 以下代码好使.

c 复制代码
int test_bio_to_buffer(void)
{
    int ok = 0;
    BIO* bio = NULL;

    char data[16];
    int i_rc = 0;

    long bio_length = 0;
    uint8_t* pBufToLoadBIO = NULL;

    do {
        bio = BIO_new_mem_buf("Hello World\n", 12);
        if (NULL == bio)
        {
            break;
        }

        // 得到BIO的数据长度, 才好去开buffer
        bio_length = bio_get_length(bio);
        assert(bio_length == 12);

        // 多开一个字节, 留一个'\0'的位置, 如果内容是可见字符, 看字符串方便
        pBufToLoadBIO = new uint8_t[bio_length + 1];
        if (NULL == pBufToLoadBIO)
        {
            break;
        }

        pBufToLoadBIO[bio_length] = '\0';

        i_rc = BIO_read(bio, pBufToLoadBIO, bio_length);
        // 如果 bio_length 太大(> imax), 再分段读来处理, 现在先这样, 就当一次就读完了
        assert(i_rc == bio_length);
        // 现在可以拿pBufToLoadBIO去干活了(自己存文件, 或作其他处理)

        ok = 1;
    } while (false);

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

    if (NULL != pBufToLoadBIO)
    {
        delete []pBufToLoadBIO;
        pBufToLoadBIO = NULL;
    }

    return ok;
}

向一个mem BIO中任意写

c 复制代码
/*!
* \file main.cpp
*/

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

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

#include "CMemHookRec.h"

void my_openssl_app();
size_t bio_get_length(BIO* bio);

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

	my_openssl_app();

	mem_unhook();

	return 0;
}

void my_openssl_app()
{
	BIO* bio = NULL;
	int i_rc = 0;
	char szBuf[1024];
	size_t szWrited = 0;
	size_t sz_tmp = 0;
	int i = 0;

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

		i_rc = BIO_test_flags(bio, BIO_FLAGS_MEM_RDONLY);
		assert(i_rc != BIO_FLAGS_MEM_RDONLY);

		for (i = 0; i < 10240; i++)
		{
			szBuf[0] = 't';
			i_rc = BIO_write_ex(bio, szBuf, sizeof(szBuf), &szWrited);
			assert((i_rc > 0) && (szWrited == sizeof(szBuf)));
			sz_tmp = bio_get_length(bio);
		}
	} while (false);

	if (NULL != bio)
	{
		BIO_free(bio);
		bio = NULL;
	}
}

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

	// 只适合与已经全部装载了mem buffer内容的BIO
	// 因为 BIO_BUF_MEM 没有对外, 所以只能通过试读来确定BIO内容的长度
	do {
		if (NULL == bio)
		{
			break;
		}

		BIO_reset(bio);
		bio_length = BIO_ctrl_pending(bio);
	} while (false);

	return bio_length;
}

END

相关推荐
石牌桥网管8 小时前
OpenSSL 生成根证书、中间证书和网站证书
网络协议·https·openssl
雨雪飘零8 小时前
Windows系统使用OpenSSL生成自签名证书
nginx·证书·openssl
许野平9 天前
OpenSSL:生成 DER 格式的 RSA 密钥对
服务器·网络·openssl·rsa·pem·der
Xnah_10 天前
ubuntu 20.4 安装 openssl 3.x
ubuntu·openssl
redwingz16 天前
openssl签名报错
openssl·random
Anlige1 个月前
PHP实现OPENSSL的EVP_BytesToKey
开发语言·php·openssl·evp
cooldream20091 个月前
升级 OpenSSL 的详细步骤(解决 SSH 漏洞的前提)
运维·ssh·openssl
年薪丰厚1 个月前
如何手动安装libcrypto.so.10和libssl.so.10这两个库?
openssl·libcrypto.so.10·libssl.so.10
恋喵大鲤鱼2 个月前
openssl(1) command
openssl
pzs02212 个月前
openssl的使用
openssl