《openssl编程》之BIO
第七章???抽象IO
7.1??? openssl抽象IO
openssl抽象IO(I/O abstraction,即BIO)是openssl對于io類型的抽象封裝,包括:內存、文件、日志、標準輸入輸出、socket(TCP/UDP)、加/解密、摘要和ssl通道等。Openssl BIO通過回調函數為用戶隱藏了底層實現細節,所有類型的bio的調用大體上是類似的。Bio中的數據能從一個BIO傳送到另外一個BIO或者是應用程序。
7.2????數據結構
?????? BIO數據結構主要有2個,在crypto/bio.h中定義如下:
?????? 1)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等函數來設置。
?????? 2)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;
?????? struct bio_st *next_bio;? /* used by filter BIOs */
?????? 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:用于存放額外數據。
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等等。
7.4????編程示例
7.4.1?? mem bio
????????????? #include <stdio.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_printf(b,"%s","zcp");
len=BIO_ctrl_pending(b);
out=(char *)OPENSSL_malloc(len);
len=BIO_read(b,out,len);
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;
??????????????????????????? }
??????????????????????????? BIO_free(b);
??????????????????????????? free(out);
???????????????????? ??? return 0;
???????????????????? }
7.4.3?? socket bio
服務端:
????????????? #include <stdio.h>
????????????? #include <openssl/bio.h>
????????????? #include <string.h>
?????????????
????????????? int???? main()
????????????? {
???????????????????? BIO????????????????????? *b=NULL,*c=NULL;
???????????????????? int???????????????????????? sock,ret,len;
???????????????????? char?????????????? *addr=NULL;
???????????????????? char?????????????? out[80];
?????????????
???????????????????? sock=BIO_get_accept_socket("2323",0);
???????????????????? b=BIO_new_socket(sock, BIO_NOCLOSE);
???????????????????? ret=BIO_accept(sock,&addr);
???????????????????? BIO_set_fd(b,ret,BIO_NOCLOSE);
???????????????????? while(1)
???????????????????? {
??????????????????????????? memset(out,0,80);
??????????????????????????? len=BIO_read(b,out,80);
??????????????????????????? if(out[0]=='q')
?????????????????????????????????? break;
??????????????????????????? printf("%s",out);
???????????????????? }
???????????????????? BIO_free(b);
???????????????????? return 0;
?????? }
?????? 客戶端telnet此端口成功后,輸入字符,服務端會顯示出來(linux下需要輸入回車)。
??????
?????? 客戶端:
#include <openssl/bio.h>
?????? int??? main()
?????? {
????????????? BIO *cbio, *out;
??????? int len;
??????? char tmpbuf[1024];
??????
??????? cbio = BIO_new_connect("localhost:http");
??????? out = BIO_new_fp(stdout, BIO_NOCLOSE);
??????? if(BIO_do_connect(cbio) <= 0)
??????? {
??????? ?????? fprintf(stderr, "Error connecting to server\n");
??????? }
??????? BIO_puts(cbio, "GET / HTTP/1.0\n\n");
??????? for(;;)
??????? {
?????????????? len = BIO_read(cbio, tmpbuf, 1024);
????????????? ?if(len <= 0) break;
?????????????? BIO_write(out, tmpbuf, len);
??????? }
??????? BIO_free(cbio);
??????? BIO_free(out);
??????? return 0;
??? }
??? 說明:本示例用來獲取本機的web服務信息。
??? cbio = BIO_new_connect("localhost:http");用來生成建立連接到本地web服務的BIO。
??? out = BIO_new_fp(stdout, BIO_NOCLOSE);生成一個輸出到屏幕的BIO。
??? BIO_puts(cbio, "GET / HTTP/1.0\n\n");通過BIO發送數據。
??? len = BIO_read(cbio, tmpbuf, 1024);將web服務響應的數據寫入緩存,此函數循環調用
??? 直到無數據。
?????? BIO_write(out, tmpbuf, len);通過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;
???????????????????? char?????????????? tmp[1024];
?
???????????????????? 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);
???????????????????? 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];
????????????? unsigned char key[8],iv[8];
?
????????????? 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);
?
????????????? /* 解密 */
????????????? 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);
????????????? return 0;
?????? }
?????? 說明:本示例采用cipher BIO對字符串"openssl"進行加密和解密,本示例編譯需要用c++編譯器;
?????? 關鍵說明:
?????? 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());
?????? ?????? 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");
??? 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;
}
?
總結
以上是生活随笔為你收集整理的《openssl编程》之BIO的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《openssl编程》之openssl简
- 下一篇: 《openssl编程》之配置文件