openssl中BIO的使用

打印 上一主题 下一主题

主题 1986|帖子 1986|积分 5958

BIO是OpenSSL的一个紧张的布局和概念,是对数据IO与传递处理的一种范例抽象和功能封装,这里所说的数据IO与传递的“介质”包罗:内存、文件、日记、标准装备、网络socket等,“处理”包罗简朴、加/解密、摘要、ssl协议下的读/写等的数据变更。
本文重要先容了BIO的布局和使用方法,并以示例方式给出了一种对BIO的封装,可为下一步与socket的干系操纵联合,编写自定义的“加密/解密流”。
本文示例实用于openssl3.0+。
1.概念

BIO是针对数据传递而设计的逻辑布局,因此它基本的概念模型是“管道”:数据由BIO一端进入,另一端流出,中间也可能举行数据变更。这样的“管道”布局可以依据是否有“缓存”,出入口等条件举行划分。
OpenSSL中将BIO分为两种范例:


  • Filter BIO:就是纯管道型BIO,数据不能保存在此中。
  • source/sink BIO:自带有容器的BIO,数据举行缓存,如果进一步细分,source BIO就是只出不进BIO,sink BIO就是只进不出BIO。

两种范例的BIO的概念示意图如上图所示。可以想象,如果将BIO首尾连接起来,就会构成BIO链,也如上图所示。BIO链在使用上,固然仅对首尾BIO举行了读写操纵,但是这种操纵是会依次传递给下一个BIO的,因而BIO链逻辑上看作是一个复杂的BIO。
2.范例

OpenSSL中已经预定义了若干种BIO,直接可以使用它们。
重要Source/Sink范例BIO


  •  BIO_s_file/ BIO_s_fd:文件BIO,BIO_s_file对应FILE*,而BIO_s_fd对应POSIX文件形貌符,它们可用于写入和读取文件。
  • BIO_s_socket:网络socketBIO,用于通过网络举行通信。
  • BIO_s_null: 空BIO,类似/dev/null,只能写入,读取数据会导致EOF。
  • BIO_s_mem:内存BIO,用于写入和读取内存。
  • BIO_s_bio:一种特殊的BIO,被称为BIO pair,后文单独说明。
重要Filter范例BIO


  • BIO_f_base64:base64 BIO,通过此BIO的BIO_write将数据编码为base64格式, BIO_read通过此BIO解码base64格式的数据。
  • BIO_f_cipher:暗码BIO,通过它的数据会被加/解密,暗码算法可以设置。
  • BIO_f_md:摘要计算BIO,它不会修改通过它的数据,而仅计算流经此中的数据摘要,摘要算法可以设置,使用特殊功能检索计算出的摘要。
  • BIO_f_buffer:缓冲BIO,它也不会更改通过它的数据。写入此BIO的数据被缓冲,因此并非每次对该BIO的写入操纵都会导致将数据写入下一个BIO。至于阅读,情况类似。这样可以减少位于缓冲IO后面的BIO上的IO操纵数。
  • BIO_f_ssl :SSL/TLS 协议BIO,通过它的数据会按照协议规则举行加解密
  • 3.基本使用函数
创建/释放函数为:
  1. BIO *BIO_new(const BIO_METHOD *)
  2. BIO_free_all(BIO *)
复制代码
设置/控制基本函数为:
  1. BIO_ctrl(BIO *,int,long ,void *)
复制代码
以此函数为基础,定义了一些方便使用的宏:BIO_reset,BIO_tell,BIO_eof,BIO_flush等
读写操纵的基本函数为:
  1. int BIO_read_ex(BIO *, void *, size_t, size_t *)
  2. int BIO_write_ex(BIO *, const void *, size_t, size_t *)
复制代码
对于BIO链,bio_st布局中有变量next_bio,prev_bio,可以指向其前后的BIO,这也是BIO链式操纵的基础。BIO链的基本操纵函数为:
  1. BIO * BIO_push(BIO *a,BIO *b);
  2. BIO * BIO_pop(BIO *b);
复制代码
前者将b链接到a之后,返回b,后者将b从链条上摘除,返回b,原来的链条依然完整。
BIO还有一些辅助函数,例如处理错误,获取状态等函数。
4.BIO Pair

BIO对是一种比较特殊的BIO,它由两个BIO构成,但从它的实当代码来看,它似乎是与BIO平行的一种实现方式,因而不能单纯的用BIO链来说明,它的逻辑布局如下图所示。

BIO pair连接两个外部端A,B,从外部来看,A端写,则可从B端读出;B端写,则可从A端读出。从内部来看,有两个内存型BIOA和BIOB,分别与A端和B端相连,它们有各自的缓存,A端存入和B端读取的数据,使用的是BIOA缓存,B端写入A端读取则使用的是BIOB的缓存。
因此BIO pair类似于“双向有缓存管道”,从任一端写入,另一端读出,由于内存缓存的使用,使得一端的读/写操纵都是“即刻”完成的,它不用关心另一端什么时候做写/读操纵,这就是典范的异步操纵。目前很多网络通信库采用的都是异步读写,因而BIO pair这种应用模型是OpenSSL适配这些网络库的一个紧张方法。
创建BIO pair的简便方法是BIO_new_bio_pair,它实际上是 BIO_new, BIO_make_bio_pair,  BIO_set_write_buf_size的组合。

5.构造自定义BIO范例

OpenSSL已定义了若干BIO,当然也可以自定义一个。下面的示例构造了一个简朴的CMYBIO,以此来说明BIO的工作原理。
  1. class CMYBIO
  2. {
  3. private:
  4.         static int write_ex(BIO* h, const char* buf, size_t num, size_t * len)
  5.         {
  6.                 printf("CMYBIO::write_ex\n");
  7.                 for (int i = 0; i < num; ++i)  printf("%c", buf[i]);
  8.                 printf("\n");
  9.                 *len = num;
  10.                 return 1;
  11.         }
  12.         static int read_ex(BIO* h, char* buf, size_t size, size_t* len)
  13.         {
  14.                 printf("CMYBIO::read_ex\n");
  15.                 unsigned int* opt = (unsigned int*)BIO_get_data(h);
  16.                 if (*opt == 0)
  17.                 {
  18.                         size = size > 6 ? 6 : size;
  19.                         memcpy(buf, "openss", size);
  20.                         *len = size;
  21. //                        BIO_clear_retry_flags(h);
  22. //                        BIO_copy_next_retry(h);
  23.                         return 1;
  24.                 }
  25.                 if (*opt == 1)
  26.                 {
  27.                         size = 9;
  28.                         memcpy(buf, "MTIzNDU2\n", size);           //base64("123456")
  29.                         *len = size;
  30.                         BIO_clear_retry_flags(h);
  31. //                        BIO_copy_next_retry(h);
  32.                         return 1;
  33.                 }
  34.                 return 0;
  35.         }
  36.         static long ctrl(BIO* h, int cmd, long arg1, void* arg2)
  37.         {
  38.                 printf("CMYBIO::ctrl[%d]\n", cmd);
  39.                 if (cmd == 0xff)
  40.                 {
  41.                         unsigned int* opt = (unsigned int*)BIO_get_data(h);
  42.                         *opt = arg1;
  43.                 }
  44.                 return 1;
  45.         }
  46.         static int create(BIO* bio)
  47.         {
  48.                 printf("CMYBIO::create\n");
  49.                 BIO_set_init(bio, 1);            //”init” must be set explicitly,otherwise “read”/”write” methods will be invoked. Although it seems odd, the “framework” would not do It for you by the returned value.
  50.                 unsigned int* opt =(unsigned int*) malloc(sizeof(int));
  51.                 *opt = 0;
  52.                 BIO_set_data(bio,opt);
  53.                 return 1;
  54.         }
  55.         static int destory(BIO* bio)
  56.         {
  57.                 printf("CMYBIO::destory\n");
  58.                 unsigned int* opt=(unsigned int*)BIO_get_data(bio);
  59.                 free(opt);
  60.                 return 1;
  61.         }
  62.         static BIO_METHOD* method;
  63. public:
  64.         static BIO_METHOD* BIO_s_my()
  65.         {
  66.                 return CMYBIO::method;
  67.         }
  68.         static void UnInit()
  69.         {
  70.                 BIO_meth_free(CMYBIO::method);
  71.         }
  72.         static void Init(void)
  73.         {
  74.                 CMYBIO::method = BIO_meth_new((100 | BIO_TYPE_SOURCE_SINK), "My BIO");
  75.                 int r;
  76.                 r = BIO_meth_set_create(CMYBIO::method, CMYBIO::create);
  77.                 r = BIO_meth_set_destroy(CMYBIO::method, CMYBIO::destory);
  78.                 r = BIO_meth_set_write_ex(CMYBIO::method,  CMYBIO::write_ex);
  79.                 r=BIO_meth_set_read_ex(CMYBIO::method, CMYBIO::read_ex);
  80.                 r=BIO_meth_set_puts(CMYBIO::method,  nullptr);
  81.                 r=BIO_meth_set_gets(CMYBIO::method, nullptr);        
  82.                 r=BIO_meth_set_ctrl(CMYBIO::method, CMYBIO::ctrl);
  83.                 r=BIO_meth_set_callback_ctrl(CMYBIO::method, nullptr);
  84.         }
  85. };
  86. BIO_METHOD* CMYBIO::method=nullptr;
复制代码
下面的代码使用了上面构造的CMYBIO,此中第一段是单独使用CMYBIO,第二段是与BIO_f_base64构成链式应用。CMYBIO作为单独应用,构造的功能已经够用,但构成链式应用,由于链式调用必要在每个具体的实现方法内来完成,因此上面的代码还不敷,为简化,这里仅把CMYBIO作为链式应用的末了一级来使用。
  1. long bio_cb(BIO* b, int oper, const char* argp, size_t len, int argi, long argl, int ret, size_t* processed)
  2. {
  3.         printf("bio callback:%d, %u\n", oper,(unsigned int)len);
  4.         return 1;
  5. }
  6. void test_bio()
  7. {
  8.         BIO* bmy = NULL;
  9.         size_t s = 0;
  10.         int len = 0;
  11.         char* out = NULL;
  12.         char cc[24];
  13.         memset(cc, 0, sizeof(cc));
  14.         CMYBIO::Init();
  15.         bmy = BIO_new(CMYBIO::BIO_s_my());
  16.         BIO_set_callback_ex(bmy, bio_cb);
  17.         len = BIO_write_ex(bmy, "openssl", 7, &s);
  18.         printf("BIO_write_ex return [%d, %u]\n\n", len, (unsigned int)s);
  19.         len = 7;
  20.         out = (char*)OPENSSL_malloc(len);
  21.         memset(out, 0, len);
  22.         len = BIO_read_ex(bmy, out, len-1, &s);
  23.         printf("BIO_read_ex return [%d,%u]%s\n", len, (unsigned int)s, out);
  24.         OPENSSL_free(out);
  25.         BIO_free(bmy);
  26.         printf("--------------------------------\n");
  27.         BIO* b64 = BIO_new(BIO_f_base64());
  28.         BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
  29.         bmy = BIO_new(CMYBIO::BIO_s_my());
  30.         BIO_push(b64, bmy);
  31.         len=BIO_write_ex(b64, "123456", 6, &s);
  32.         printf("BIO_write_ex return [%d, %u]\n\n", len, (unsigned int)s);
  33.         BIO_flush(b64);             //important!!!
  34.         BIO_ctrl(bmy, 0xff, 1, nullptr);
  35.         len = BIO_read_ex(b64, cc, sizeof(cc)- 1, &s);
  36.         printf("BIO_read_ex return [%d,%u]%s\n\n", len, (unsigned int)s, cc);
  37.         //OPENSSL_free(out);
  38.         BIO_free_all(b64);
  39.         CMYBIO::UnInit();
  40. }
复制代码
整个过程输出如下图所示。前一个示例重要展示CMYBIO中各方法的调用过程,此中还设置了“钩子”函数,从输出可以清晰看出BIO的工作过程。后一个示例稍有些贫苦,此中增长了CMYBIO内部的设置,从而可以实现不同的效果。

必要注意的是,BIO_f_base64有其特殊之处,对于“write”,必要在完成后调用flush才能精确工作,对于“read”,编码串结束的标志是“\n”,但还是要设置BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL),否则可能会有错误。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美食家大橙子

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表