openssl3.2 - exp - buf to bio

发布于:2024-02-27 ⋅ 阅读:(139) ⋅ 点赞:(0)

openssl3.2 - exp - buf to bio

概述

不想让程序调用openssl API时, 有文件落地的动作.
如果程序有配置文件要用, 也是自己读文件到buffer, 然后转成BIO给openssl的相关有BIO入参的API用.
如果在程序中用数组定义了一些PEM内容的数组, 也可以将数组转成BIO来用.

从openssl测试代码中, 找到了BIO_new_mem_buf(), 可以做这个事情.
那么程序需要的文件输入, 都可以读入buffer, 转成BIO, 后续都用BIO来处理.

笔记

/*!
* \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内的数据长度是确定的.
翻翻openssl实现, 感觉也就只能通过试读来确定BIO的数据长度.
封装了一个函数bio_get_length(), 好使.

调用端代码

        // 如果是文件输入, 可以自己读文件到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);

函数实现

long bio_get_length(BIO* bio)
{
    long bio_length = 0;
    uint8_t* pBuf = NULL;
    int buf_len = 1024 * 1024;
    int i_rc = 0;

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

        pBuf = (uint8_t*)OPENSSL_malloc(buf_len);
        if (NULL == pBuf)
        {
            break;
        }


        BIO_reset(bio);

        do {
            i_rc = BIO_read(bio, pBuf, buf_len);
            if (i_rc <= 0)
            {
                break;
            }

            bio_length += i_rc;
            if (i_rc != buf_len)
            {
                break;
            }
        } while (true);
        
        BIO_reset(bio);
    } while (false);

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

    return bio_length;
}

bio_to_buffer

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

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;
}

END

本文含有隐藏内容,请 开通VIP 后查看