openssl bio

bio介绍

bio是openssl封装的io库,类型分为source/sink 和filter两种类型
bss_开头的文件是source/sink类型的,bf_开头的是filter类型(主要包括对数据的处理,比如base64编码,cipher加解密等)。网上的介绍很多了,这里主要通过几个测试例子来验证和测试在使用过程中需要注意的事项。
以下例子使用openssl1.1.1m测试通过

#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>

//内存bio使用
void membio()
{
    BIO *bio = BIO_new(BIO_s_mem());
    BIO_write(bio, "openssl", 4);
    BIO_printf(bio, "%s", "hellow");
    size_t len = BIO_ctrl_pending(bio); //返回pending的数量
    if (len > 0)
    {
        printf("%ld\n", len); //长度为10
        char *buf = (char *)OPENSSL_malloc(len + 1);
        memset(buf, 0, len + 1);
        int readnum = BIO_read(bio, buf, len); //传多大空间读多少数据
        printf("%s\n", buf);
    }
}
//文件bio使用
void filebio()
{
    // BIO * fbio=BIO_new(BIO_s_file());
    // BIO_write_filename(fbio,"xx.log");
    // BIO_read_filename(fbio,"xx.log");

    BIO *bio = BIO_new_file("xx.log", "r");
    BIO *wbio = BIO_new_file("yy.log", "a+");
    //    size_t len=BIO_ctrl_pending(bio);文件类型的bio测试BIO_ctrl_pending只能获取到0。
    //    int len =BIO_pending(bio);//文件类型的bio测试bio_pending只能获取到0。得到BIO中读缓存或写缓存中字符的数目
    // 似乎不能获取到文件长度
    char buf[10] = {0};
    while (BIO_eof(bio) != 1) // 1代表读到了末尾
    {
        int len = BIO_gets(bio, buf, 5);
        //传5个只读四个,内部使用的是c语言的fgets,这样如果传5只能读取4个字符,其他的用\0填充,比较安全
        // c中的gets方法会丢弃回车即它不检查预留存储区是否能够容纳实际输入的数据,换句话说,如果输入的字符数目大于数组的长度,gets 无法检测到这个问题,就会发生内存越界,所以编程时建议使用 fgets()。
        if (len > 0)
        {
            // printf("%s\n", buf);
            BIO_printf(wbio, "%s", buf);
        }
    }
    BIO_free(wbio);
    BIO_free(bio);
}
//socket bio使用
void socket_server_bio()
{

    int socket = BIO_get_accept_socket("5566", 0);
    BIO *bio = BIO_new_socket(socket, BIO_NOCLOSE);
    char *addr;
    int ac_sock = BIO_accept(socket, &addr);
    BIO_set_fd(bio, ac_sock, BIO_NOCLOSE);
    unsigned char buf[1024];
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        int len = BIO_read(bio, buf, sizeof(buf));
        if (len > 0)
        {
            if (buf[0] == 'q')
            {
                break;
            }
            else
            {
                printf("%s\n", buf); // read可能会读1024个,be care
            }
        }
        else
        {
            break;
        }
    }
}

void socket_bio_client()
{
    BIO *bio, *outbio;
    bio = BIO_new_connect("127.0.0.1:9090"); //可以是IP加端口
    outbio = BIO_new_fd(1, BIO_NOCLOSE);     // 0标准输入,1标准输出
    if (BIO_do_connect(bio) <= 0)
    {
        printf("connect fail");
        goto end;
    };
    char buf[1024];
    BIO_puts(bio, "GET / HTTP/1.0\n\n");
    while (1)
    {
        int len = BIO_read(bio, buf, sizeof(buf));
        if (len <= 0)
            break;
        BIO_write(outbio, buf, len);
        BIO_flush(outbio);
    }
end:
    BIO_free(bio);
    BIO_free(outbio);
}
//摘要bio
void bioDigist()
{
    // md5,base64,cipher这些bio可能没有puts,gets方法,如要要使用这些方法,需要给他附加一个BIO_f_buffer,
    // md有gets无puts
    BIO *bio = BIO_new(BIO_f_md());
    BIO_set_md(bio, EVP_md5());
    BIO *fbio = BIO_new_file("xx.txt", "wb");
    //    BIO * nullbio=BIO_new(BIO_s_null());
    // BIO_new(BIO_f_null());//什么也不做,传给下一个bio,和BIO_s_null不同,他直接丢弃
    // BIO_new(BIO_f_buffer());
    //    bio= BIO_push(bio,nullbio);
    BIO_push(bio, fbio);
    char data[] = "adfdsfdsf";
    BIO_write(bio, (const void *)data, strlen(data)); //对称BIO,read write应该都一样
    //这个bio_md.c中的write方法如果有next,先把数据写到next,也就是我这里的写入文件的数据是原始数据,然后调用了EVP_DigestUpdate

    BIO_flush(bio);
    char buf[64];
    memset(buf, 0, 64);

    int len = BIO_gets(bio, buf, 64); //此方法才会调用EVP_DigestFinal_ex,所以想把进行摘要后保存到文件只能获取这个值自己保存到文件?
    // printf("%d,%s\n",len,buf);
    char *mdstr = OPENSSL_buf2hexstr(buf, (long)len);
    printf("%s\n", mdstr);
    // char buf[64];
    // memset(buf, 0, sizeof(buf));
    // BIO_read(bio, buf, 64);
    // printf("%s\n", buf);
    BIO_free_all(bio);
}
//加解密bio
void bio_cipher()
{
    BIO *bio = BIO_new(BIO_f_cipher()); // bio_enc.c
    const EVP_CIPHER *cipher = EVP_aes_128_cbc();
    BIO_set_cipher(bio, cipher, "0123456789ABCDEF", "0123456789ABCDEF", 1);
    const char *data = "hello";
    BIO *cbio = BIO_new_file("cipher.txt", "w");
    BIO_push(bio, cbio);

    //注意如果没有next bio直接返回,也就是他必须把加密结果输出到一个地方。
    //将数据进行加密EVP_CipherUpdate,然后把加密的数据写入到下一个bio,加密一段数据加密的结果在结构体BIO_ENC_CTX中
    //每次调用EVP_CipherUpdate(cipher,out,outlen,in ,inlen);都会有输出。
    //那什么时候调用EVP_CipherFinal_ex呢,也就是缓存中还剩下的未加密的数据,现在在flush的时候又调用,
    //在read的时候也会调用
    //也就是说读是从下一个bio中读,读完之后需要进行加解密(至于是加密还是解密时通过上面的BIO_set_cipher方法中的参数决定的)
    //当读完了数据不够加解密分组的时候会调用EVP_CipherFinal_ex(不够分组进行填充)
    //写只有在flush的时候才看缓存中数据长度不够分组的时候调用EVP_CipherFinal_ex

    BIO_write(bio, data, strlen(data));
    BIO_flush(bio); //写数据必须调用flush

    //----------------------------------------------
    //解密
    BIO *denBIO = BIO_new(BIO_f_cipher());
    const EVP_CIPHER *denCipher = EVP_aes_128_cbc();
    BIO_set_cipher(denBIO, denCipher, "0123456789ABCDEF", "0123456789ABCDEF", 0);
    BIO *rbio = BIO_new_file("cipher.txt", "r");
    BIO_push(denBIO, rbio);
    char buf[1024];
    memset(buf, 0, 1024);
    int readlen = BIO_read(denBIO, buf, 1024);//这里做成循环读取,返回小于等于0时退出。
    printf("%s\n",buf);
    BIO_free_all(bio);
    BIO_free_all(denBIO);
}
//base64编码bio
void bio_base64(){
    #if 0
    BIO * bio=BIO_new(BIO_f_base64());
    BIO *outfile=BIO_new_file("base64","w");
    BIO_push(bio,outfile);
    char *data="abcdefg打开";
    BIO_write(bio,data,strlen(data));//也是需要一个next 否则直接返回,写是编码,读是解码
    BIO_flush(bio);//需要调用flush
    BIO_free_all(bio);
    #endif 
    //---------------------------------------
    BIO * bio=BIO_new(BIO_f_base64());
    BIO *outfile=BIO_new_file("base64","r");
    BIO_push(bio,outfile);
    char buf[1024];
    memset(buf,0,1024);
    BIO_read(bio,buf,1024);
    printf("%s\n",buf);
    BIO_free_all(bio);
}

int main()
{
    // filebio();

    //  socket_server_bio();

    //   socket_bio_client();
    // bio 分为source/sink系列和filter系列两种。
    //bioDigist();
    // bio_cipher();
    bio_base64();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值