IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 2021SC@SDUSC openssl-gpu 抽象IO -> 正文阅读

[网络协议]2021SC@SDUSC openssl-gpu 抽象IO

2021SC@SDUSC
7 抽象IO
7.1 openssl 抽象 IO

openssl 抽象 IO(I/O abstraction,即 BIO)是 openssl 对于 io 类型的抽象封装,
包括:内存、 文件、日志、标准输入输出、socket(TCP/UDP)、加/解密、摘要和 ssl 通道等。
Openssl BIO 通过回调函数为用户隐藏了底层实现细节,所有类型的 bio 的调用大体上是类似的。
Bio 中的数据能从一个 BIO 传送到另外一个 BIO 或者是应用程序。

其实包含了很多种接口,用通用的函数接口,主要控制在BIO_METHOD中的不同实现函数控制,
包括6种filter型和8种source/sink型。

source/sink类型的BIO是数据源,
例如,sokect BIO 和 文件BIO。
而filter BIO就是把数据从一个BIO转换到另外一个BIO或应用接口,
在转换过程中,些数据可以不修改(如信息摘要BIO),也可以进行转换.
例如在加密BIO中,如果写操作,数据就会被加密,如果是读操作,数据就会被解密。

BIO是封装了许多类型I/O接口细节的一种应用接口,
可以和SSL连接、 非加密的网络连接以及文件IO进行透明的连接。
BIO可以连接在一起成为一个BIO链(单个的BIO就是一个环节的BIO链的特例),

如下是BIO的结构定义,可以看到它有上下环节。

一个BIO链通常包括一个source BIO和一个或多个filter BIO,数据从第一个BIO读出或写入,
然后经过一系列BIO变化到输出(通常是一个source/sink BIO)。

BIO目录文件的简要说明:

bio.h: 主定义的头文件,包括了很多通用的宏的定义。
bio_lib.c: 主要的BIO操作定义文件,是比较上层的函数了。
bss_*系列:是soruce/sink型BIO具体的操作实现文件
bf_*系列: 是filter型BIO具体的操作实现文件
bio_err.c: 是错误信息处理文件
bio_cb.c: 是callback函数的相关文件
b_print.c: 是信息输出的处理函数文件
b_socket.c: 是Socket连接的一些相关信息处理文件
b_dump.c: 是对内存内容的存储操作处理

7.2 数据结构

BIO 数据结构主要有 2 个,在 crypto/bio.h 中定义如下:

    BIO_METHOD
typedef struct bio_method_st {
        int type;
        const char *name;
        int (*bwrite)(BIO *, const char *, int);
        int (*bread)(BIO *, char *, int);
        int (*bputs)(BIO *, const char *);
        int (*bgets)(BIO *, char *, int);
        long (*ctrl)(BIO *, int, long, void *);
        int  (*create)(BIO *);
        int  (*destroy)(BIO *);
        long (*callback_ctrl)(BIO *, int, bio_info_cb *);
} BIO_METHOD;

该结构定义了 IO 操作的各种回调函数,根据需要,具体的 bio 类型必须实现其中的一种或多种回调函数,各项意义如下:

type: 具体 BIO 类型;
name: 具体 BIO 的名字;
bwrite: 具体 BIO 写操作回调函数;
bread: 具体 BIO 读操作回调函数;
bputs: 具体 BIO 中写入字符串回调函数;
bgets: 具体 BIO 中读取字符串函数;
ctrl: 具体 BIO 的控制回调函数;
create: 生成具体 BIO 回调函数;
destroy: 销毁具体 BIO 回调函数;
callback_ctrl: 具体 BIO 控制回调函数,与 ctrl 回调函数不一样,
该函数可由调用者(而不是实现者)来实现,然后通过
BIO_set_callback 等函数来设置。

    BIO
truct bio_st {
    BIO_METHOD *method;
    /* bio, mode, argp, argi, argl, ret */
    long (*callback)(struct bio_st *,int,const char *,int, long,long);
    char *cb_arg;   /* first argument for the callback */
    int  init;
    int  shutdown;
    int  flags;      /* extra storage */
    int  retry_reason;
    int  num;
    void *ptr;
    structbio_st *next_bio;   /*usedbyfilterBIOs*/
    struct bio_st *prev_bio; /* used by filter BIOs */
    int references;
    nsigned long  num_read;
    unsigned long num_write;
    CRYPTO_EX_DATA ex_data;
};

/*
主要项含义:

init: 具体句柄初始化标记,初始化后为1。
比如:文件 BIO 中,通过 BIO_set_fp 关联一个文件指针时,该标记则置 1 ;
socket BIO中,通过 BIO_set_fd 关联一个链接时,设置该标记为 1。

shutdown: BIO 关闭标记,当该值不为 0 时,释放资源; 该值可以通过控制函 数来设置。

flags: 有些 BIO 实现需要它来控制各个函数的行为。
比如文件 BIO 默认该值为 BIO_FLAGS_UPLINK,
这时文件读操作调用UP_fread 函数而不是调用fread 函数。

retry_reason: 重试原因,主要用在 socket 和 ssl BIO 的异步阻塞。
比如 socket bio 中,遇到 WSAEWOULDBLOCK 错误时,
openssl 告诉用户的操作需要重试。

num: 该值因具体 BIO 而异,比如 socket BIO 中 num 用来存放链接字。

ptr: 指针,体 bio 有不同含义。比如:
文件 BIO 中它用来存放文件句柄;
mem BIO 中它用来存放内存地址;
connect BIO 中它用来存放 BIO_CONNECT 数据,
accept BIO 中它用来存放 BIO_ACCEPT 数据。

next_bio: 下一个 BIO 地址,BIO 数据可以从一个BIO传送到另一个BIO,
该值指明了下一个 BIO 的地址。

references: 被引用数量。
num_read: BIO 中已读取的字节数。
num_write: BIO 中已写入的字节数。
ex_data: 用于存放额外数据。
*/

typedef struct bio_st BIO;

struct bio_st
{
     BIO_METHOD *method; //BIO方法结构,是决定BIO类型和行为的重要参数,各种BIO的不同之处主要也正在于此项。

     long (*callback)(struct bio_st *,int,const char *,int, long,long); //BIO回调函数
     char *cb_arg;                                                      //回调函数的第一个参量

     int init;         //初始化标志,初始化了为1,否则为0。比如文件BIO 中,通过BIO_set_fp
                       //关联一个文件指针时,该标记则置1。
     int shutdown;     //BIO开关标志,如果为BIO_CLOSE,则释放BIO时自动释放持有的资源,否则不自动释放持有资源
     int flags;        //有些BIO 实现需要它来控制各个函数的行为。比如文件BIO 默认该值为BIO_FLAGS_UPLINK,
                       //这时文件读操作调用UP_fread 函数而不是调用fread 函数。
     int retry_reason; //重试原因,主要用在socket 和ssl BIO 的异步阻塞。比如socketbio 中,遇到
                       //WSAEWOULDBLOCK 错误时,openssl 告诉用户的操作需要重试
     int num;          //该值因具体BIO 而异,比如socket BIO 中num 用来存放链接字。
     void *ptr;        //ptr:指针,具体bio 有不同含义。比如文件BIO中它用来存放文件句柄;mem bio 中它用来存放
                       //内存地址;connect bio 中它用来存放BIO_CONNECT 数据,acceptbio 中它
                       //用来存放BIO_ACCEPT数据。

     struct bio_st *next_bio; //BIO链中下一个BIO 地址,BIO 数据可以从一个BIO 传送到另一个BIO。
     struct bio_st *prev_bio; //BIO链中上一个BIO 地址,

     int references;          //引用计数
     unsigned long num_read;  //已读出的数据长度
     unsigned long num_write; //已写入的数据长度

     CRYPTO_EX_DATA ex_data;  //额外数据
};

7.3 BIO 函数

BIO 各个函数定义在 crypto/bio.h 中。所有的函数都由 BIO_METHOD 中的回调函 数来实现。函数主要分为几类:

  1. 具体BIO相关函数
    比如:BIO_new_file(生成新文件)和 BIO_get_fd(设置网络链接)等。

  2. 通用抽象函数
    比如 BIO_read 和 BIO_write 等。

另外,有很多函数是由宏定义通过控制函数 BIO_ctrl 实现,
比如 BIO_set_nbio、BIO_get_fd 和 BIO_eof 等等。

在BIO的所用成员中,method可以说是最关键的一个成员,它决定了BIO的类型,
可以看到,在定义一个新的BIO结构时,总是使用下面的函数:

BIO* BIO_new(BIO_METHOD *type);

在源代码可以看出,BIO_new函数除了给一些初始变量赋值外,
主要就是把type中的各个变量赋值给BIO结构中的method成员。
一般来说,上述type参数是以一个返回值为BIO_METHOD类型的函数提供的,
如生成一个mem型的BIO结构,就使用下面的语句:

BIO *mem = BIO_new(BIO_s_mem());

// 【source/sink型】
BIO_METHOD* BIO_s_accept() //一个封装了类似TCP/IP socket Accept规则的接口,并且使TCP/IP操作对于BIO接口透明。
BIO_METHOD* BIO_s_connect() //一个封装了类似TCP/IP socket Connect规则的接口,并且使TCP/IP操作对于BIO接口透明
BIO_METHOD* BIO_s_socket() //封装了socket接口的BIO类型

BIO_METHOD* BIO_s_bio() //封装了一个BIO对,数据从其中一个BIO写入,从另外一个BIO读出

BIO_METHOD* BIO_s_fd() //是一个封装了文件描述符的BIO接口,提供类似文件读写操作的功能
BIO_METHOD* BIO_s_file() //封装了标准的文件接口的BIO,包括标准的输入输出设备如stdin等
BIO_METHOD* BIO_s_mem() //封装了内存操作的BIO接口,包括了对内存的读写操作
BIO_METHOD* BIO_s_null() //返回空的sink型BIO接口,写入这种接口的所有数据读被丢弃,读的时候总是返回EOF

//【filter型】
BIO_METHOD* BIO_f_base64() //封装了base64编码方法的BIO,写的时候进行编码,读的时候解码
BIO_METHOD* BIO_f_cipher() //封装了加解密方法的BIO,写的时候加密,读的时候解密
BIO_METHOD* BIO_f_md() //封装了信息摘要方法的BIO,通过该接口读写的数据都是已经经过摘要的。
BIO_METHOD* BIO_f_ssl() //封装了openssl 的SSL协议的BIO类型,也就是为SSL协议增加了一些BIO操作方法。

BIO_METHOD* BIO_f_null() //一个不作任何事情的BIO,对它的操作都简单传到下一个BIO去了,相当于不存在。

BIO_METHOD* BIO_f_buffer() //封装了缓冲区操作的BIO,写入该接口的数据一般是准备传入下一个BIO接口的,从该——
//接口读出的数据一般也是从另一个BIO传过来的

7.4 编程示例
7.4.1 mem BIO

#include <stdio.h> 
#include <string.h> 
#include <openssl/bio.h>

int main() 
{
	BIO *b=NULL; 
	int len=0;
	char *out=NULL;

	b=BIO_new(BIO_s_mem()); 
	len=BIO_write(b,"openssl",4);
	//len=BIO_write(b,"openssl",7);
	len=BIO_printf(b,"%s","zcp"); 
	len=BIO_ctrl_pending(b);
	out=(char *)OPENSSL_malloc(len); 
	len=BIO_read(b,out,len); 
	printf(" %zu :  %s \n" ,strlen(out), out);
	//printf(" %d :  %s \n" ,len, out);

	OPENSSL_free(out);
	BIO_free(b);
	return 0; 
}

说明:
b=BIO_new(BIO_s_mem()); 生成一个mem 类型的BIO。
len=BIO_write(b,“openssl”,7); 将字符串"openssl"写入 bio。
len=BIO_printf(b,“bio test”,8); 将字符串"bio test"写入 bio。
len=BIO_ctrl_pending(b); 得到缓冲区中待读取大小。
len=BIO_read(b,out,50); 将bio中的内容写入out缓冲区。

7.4.2 file bio

#include <stdio.h> 
#include <openssl/bio.h>
int main()
{
	BIO *b=NULL; 
	int len=0,outlen=0;

	char *out=NULL;

	b=BIO_new_file("bf.txt","w"); 
	len=BIO_write(b,"openssl",4); 
	len=BIO_printf(b,"%s","zcp"); 
	BIO_free(b); 

	b=BIO_new_file("bf.txt","r"); 
	len=BIO_pending(b);
	len=50;
	out=(char *)OPENSSL_malloc(len); 
	len=1;
	while(len>0) {
		len=BIO_read(b,out+outlen,1);
		outlen+=len; 
	}
	printf("outlen = %d \n" , outlen);

	BIO_free(b); 
	free(out); 
	return 0;
}

7.4.3 socket BIO

( 待补充 )
7.4.4 md BIO

#include <openssl/bio.h> 
#include <openssl/evp.h>

int main()
{

	BIO *bmd=NULL,*b=NULL;
	const  EVP_MD *md=EVP_md5();
	int len, i;  
	char tmp[1024]={0};

	bmd=BIO_new(BIO_f_md()); 
	BIO_set_md(bmd,md);
	b= BIO_new(BIO_s_null()); 
	b=BIO_push(bmd,b); 
	len=BIO_write(b,"openssl",7); 
	len=BIO_gets(b,tmp,1024); 
	

	for ( i=0 ; i < 16; i++)
		printf("0x%02x: ", (uint8_t)tmp[i]);
	BIO_free(b);
	return 0;
}

说明: 本示例用 md BIO 对字符串"opessl"进行 md5 摘要。
bmd=BIO_new(BIO_f_md());生成一个 md BIO。
BIO_set_md(bmd,md); 设置 md BIO 为 md5 BIO。
b= BIO_new(BIO_s_null()); 生成一个 null BIO。
b=BIO_push(bmd,b); 构造BIO 链,md5 BIO 在顶部。
len=BIO_write(b,“openssl”,7); 将字符串送入 BIO 做摘要。
len=BIO_gets(b,tmp,1024); 将摘要结果写入 tmp 缓冲区

7.4.5 cipher BIO

#include <string.h> 
#include <openssl/bio.h> 
#include <openssl/evp.h>

int main()
{
	/* 加密 */ 
	BIO *bc=NULL,*b=NULL; 
	const EVP_CIPHER *c = EVP_des_ecb(); 
	int len,i;
	char tmp[1024]={0};
	unsigned char key[8],iv[8];

	/* init key iv */
	for(i=0;i<8;i++) {
		memset(&key[i],i+1,1);
		memset(&iv[i], i+1,1); 
	}

	bc=BIO_new(BIO_f_cipher());
	BIO_set_cipher(bc,c,key,iv,1); 
	b= BIO_new(BIO_s_null());
	b=BIO_push(bc,b); 
	len=BIO_write(b,"openssl",7); 
	len=BIO_read(b,tmp,1024); 
	BIO_free(b);

	/*print */
	for (i =0; i < 8; i++){
		printf("%x ", (uint8_t) tmp[i]);
	}
	printf("\n");

	/* 解密 */
	BIO *bdec=NULL,*bd=NULL;
	const EVP_CIPHER*cd=EVP_des_ecb();

	bdec=BIO_new(BIO_f_cipher()); 
	BIO_set_cipher(bdec,cd,key,iv,0); 
	bd= BIO_new(BIO_s_null()); 
	bd=BIO_push(bdec,bd); 
	len=BIO_write(bdec,tmp,len);

	len=BIO_read(bdec,tmp,1024); 
	BIO_free(bdec);

	/*print */
	tmp[len]='\0';
	printf("len = %d, %s \n", len, tmp);

	return 0;
}

说明:本示例采用 cipher BIO 对字符串"openssl"进行加密和解密;
关键说明:
BIO_set_cipher(bc,c,key,iv,1);设置加密 BI。
BIO_set_cipher(bdec,cd,key,iv,0);设置解密 BIO。
其中 key 为对称密钥,iv 为初始化向量。 加/解密结果通过 BIO_read 获取。

7.4.6 SSl BIO

#include <openssl/bio.h>
#include <openssl/ssl.h>

int     main()
{
	BIO*sbio, *out;
	int len;
	char tmpbuf[1024];
	SSL_CTX *ctx;
	SSL *ssl;

	SSLeay_add_ssl_algorithms();
	OpenSSL_add_all_algorithms();
	//ctx = SSL_CTX_new(SSLv3_client_method());
	ctx = SSL_CTX_new(SSLv23_client_method());
	sbio = BIO_new_ssl_connect(ctx);
	BIO_get_ssl(sbio, &ssl);
	if(!ssl) {
		fprintf(stderr, "Can not locate SSL pointer\n");
		return 0;
	}
	SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
	//BIO_set_conn_hostname(sbio, "mybank.icbc.com.cn:https");
	BIO_set_conn_hostname(sbio, "www.baidu.com:https");
	out = BIO_new_fp(stdout, BIO_NOCLOSE);
	BIO_printf(out,"链接中...\n");
	if(BIO_do_connect(sbio) <= 0) {
		fprintf(stderr, "Error connecting to server\n");
		return 0;
	}
	if(BIO_do_handshake(sbio) <= 0){
		fprintf(stderr, "Error establishing SSL connection\n");
		return 0;
	}
	BIO_puts(sbio, "GET / HTTP/1.0\n\n");
	for(;;)
	{
		len = BIO_read(sbio, tmpbuf, 1024);
		if(len <= 0) break;
		BIO_write(out, tmpbuf, len);
	}
	BIO_free_all(sbio);
	BIO_free(out);
	return 0;
}
 
/*
 * 本函数用ssl bio来链接mybank.icbc.com.cn的https服务,并请求首页文件。
 * 其中SSLeay_add_ssl_algorithms和OpenSSL_add_all_algorithms函数必不可少,
 * 否则不能找到ssl加密套件并且不能找到各种算法。
 */

7.4.7 其他

#include <openssl/bio.h>
#include <openssl/asn1.h>
int main()
{
	int ret,len,indent;
	BIO *bp;
	char *pp,buf[5000];
	FILE  *fp;

	bp = BIO_new(BIO_s_file());
	BIO_set_fp(bp,stdout,BIO_NOCLOSE);
	fp = fopen("der.cer","rb");
	len = fread(buf,1,5000,fp);
	fclose(fp);
	pp = buf;
	indent = 5;
	ret = BIO_dump_indent(bp,pp,len,indent);
	BIO_free(bp);
	return 0;
}
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-12-07 12:22:55  更:2021-12-07 12:24:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年7日历 -2024/7/8 10:07:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码