日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

用OpenSSL编写SSL,TLS程序

發布時間:2023/12/10 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用OpenSSL编写SSL,TLS程序 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://zhoulifa.bokee.com/6134045.html

http://blog.sina.com.cn/s/blog_86ca13bb0100vaph.html

http://blog.chinaunix.net/uid-26575352-id-3048856.html

一、簡介:

SSL(SecureSocket Layer)是netscape公司提出的主要用于web的安全通信標準,分為2.0版和3.0版.TLS(TransportLayer Security)是IETF的TLS工作組在SSL3.0基礎之上提出的安全通信標準,目前版本是1.0,即RFC2246.SSL/TLS提供的安全機制可以保證應用層數據在互聯網絡傳輸不 被監聽,偽造和竄改.

openssl(www.openssl.org)是sslv2,sslv3,tlsv1的一份完整實現,內部包含了大量加密算法程序.其命令行提供了豐富的加密,驗證,證書生成等功能,甚至可以用其建立一個完整的CA.與其同時,它也提供了一套完整的庫函數,可用開發用SSL/TLS的通信程序.Apache的https兩種版本mod_ssl和apachessl均基于它實現的.openssl繼承于ssleay,并做了一定的擴展,當前的版本是0.9.5a.

openssl的缺點是文檔太少,連一份完整的函數說明都沒有,man page也至今沒做完整:-(,如果想用它編程序,除了熟悉已有的文檔(包括ssleay,mod_ssl,apachessl的文檔)外,可以到它的maillist上找相關的帖子,許多問題可以在以前的文章中找到答案.

基于OpenSSL的程序都要遵循以下幾個步驟:

(1 ) OpenSSL初始化

在使用OpenSSL之前,必須進行相應的協議初始化工作,這可以通過下面的函數實現:

int SSL_library_init(void);

(2 ) 選擇會話協議

在利用OpenSSL開始SSL會話之前,需要為客戶端和服務器制定本次會話采用的協議,目前能夠使用的協議包括TLSv1.0、SSLv2、SSLv3、SSLv2/v3。

需要注意的是,客戶端和服務器必須使用相互兼容的協議,否則SSL會話將無法正常進行。

(3 ) 創建會話環境

在OpenSSL中創建的SSL會話環境稱為CTX,使用不同的協議會話,其環境也不一樣的。

申請SSL會話環境的OpenSSL函數是:

SSL_CTX *SSL_CTX_new(SSL_METHOD * method);

當SSL會話環境申請成功后,還要根據實際的需要設置CTX的屬性,通常的設置是指定SSL握手階段證書的驗證方式和加載自己的證書。

制定證書驗證方式的函數是:

int SSL_CTX_set_verify(SSL_CTX *ctx,intmode,int(*verify_callback),int(X509_STORE_CTX *));

為SSL會話環境加載CA證書的函數是:

SSL_CTX_load_verify_location(SSL_CTX *ctx,const char *Cafile,constchar *Capath);

為SSL會話加載用戶證書的函數是:

SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file,inttype);

為SSL會話加載用戶私鑰的函數是:

SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx,const char* file,inttype);

在將證書和私鑰加載到SSL會話環境之后,就可以調用下面的函數來驗證私鑰和證書是否相符:

int SSL_CTX_check_private_key(SSL_CTX *ctx);

(4) 建立SSL套接字

SSL套接字是建立在普通的TCP套接字基礎之上,在建立SSL套接字時可以使用下面的一些函數:

SSL *SSl_new(SSL_CTX *ctx);??? //申請一個SSL套接字

int SSL_set_fd(SSL *ssl,int fd);??? //綁定讀寫套接字

int SSL_set_rfd(SSL *ssl,int fd);??? //綁定只讀套接字

int SSL_set_wfd(SSL *ssl,int fd);??? //綁定只寫套接字

(5) 完成SSL握手

在成功創建SSL套接字后,客戶端應使用函數SSL_connect( )替代傳統的函數connect( )來完成握手過程:

int SSL_connect(SSL *ssl);

而對服務器來講,則應使用函數SSL_ accept ( )替代傳統的函數accept ( )來完成握手過程:

int SSL_accept(SSL *ssl);

握手過程完成之后,通常需要詢問通信雙方的證書信息,以便進行相應的驗證,這可以借助于下面的函數來實現:

X509 *SSL_get_peer_certificate(SSL *ssl);

該函數可以從SSL套接字中提取對方的證書信息,這些信息已經被SSL驗證過了。

X509_NAME *X509_get_subject_name(X509 *a);

該函數得到證書所用者的名字。

(6) 進行數據傳輸

當SSL握手完成之后,就可以進行安全的數據傳輸了,在數據傳輸階段,需要使用SSL_read( )和SSL_write()來替代傳統的read( )和write( )函數,來完成對套接字的讀寫操作:

int SSL_read(SSL *ssl,void *buf,int num);

int SSL_write(SSL *ssl,const void *buf,int num);

(7 ) 結束SSL通信

當客戶端和服務器之間的數據通信完成之后,調用下面的函數來釋放已經申請的SSL資源:

int SSL_shutdown(SSL *ssl);??? //關閉SSL套接字

void SSl_free(SSL *ssl);??? //釋放SSL套接字


void SSL_CTX_free(SSL_CTX *ctx);???? //釋放SSL會話環境


客戶端與服務端編程框架:

程序分為兩部分,客戶端和服務器端,我們的目的是利用SSL/TLS的特性保證通信雙方能夠互相驗證對方身份(真實性),并保證數據的完整性, 私密性.


1.客戶端程序的框架為:

/*生成一個SSL結構*/
meth = SSLv23_client_method();
ctx = SSL_CTX_new (meth);
ssl = SSL_new(ctx);

/*下面是正常的socket過程*/
fd = socket();
connect();

/*把建立好的socket和SSL結構聯系起來*/
SSL_set_fd(ssl,fd);

/*SSL的握手過程*/
SSL_connect(ssl);

/*接下來用SSL_write(), SSL_read()代替原有的write(),read()即可*/
SSL_write(ssl,”Hello world”,strlen(“Hello World!”));

2.服務端程序的框架為:
/*生成一個SSL結構*/
meth = SSLv23_server_method();
ctx = SSL_CTX_new (meth);
ssl = SSL_new(ctx);

/*下面是正常的socket過程*/
fd = socket();
bind();
listen();
accept();

/*把建立好的socket和SSL結構聯系起來*/
SSL_set_fd(ssl,fd);

/*SSL的握手過程*/
SSL_connect(ssl);

/*接下來用SSL_write(), SSL_read()代替原有的write(),read()即可*/
SSL_read (ssl, buf, sizeof(buf));

根據RFC2246(TLS1.0)整個TLS(SSL)的流程如下:

Client????????????????????????????????????????????????Server

ClientHello?????????? ——–>
????????????????????????????????????????????????ServerHello
???????????????????????????????????????????????? Certificate*
??????????????????????????????????????????ServerKeyExchange*
????????????????????????????????????????????CertificateRequest*
?????????????????????????? <——–??????ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished?????????????? ——–>
???????????????????????????????????????????? [ChangeCipherSpec]
????????????????????????????<——–???????????? Finished
Application Data?? <——->???? Application Data

對程序來說,openssl將整個握手過程用一對函數體現,即客戶端的SSL_connect和服務端的SSL_accept.而后的應用層數據交換則用SSL_read和 SSL_write來完成.

二、證書文件生成

除將程序編譯成功外,還需生成必要的證書和私鑰文件使雙方能夠成功驗證對方,步驟如下:

1.首先要生成服務器端的私鑰(key文件):
openssl genrsa -des3 -out server.key 1024
運行時會提示輸入密碼,此密碼用于加密key文件(參數des3便是指加密算法,當然也可以選用其他你認為安全的算法.),以后每當需讀取此文 件(通過openssl提供的命令或API)都需輸入口令.如果覺得不方便,也可以去除這個口令,但一定要采取其他的保護措施!
去除key文件口令的命令:
openssl rsa -in server.key -out server.key

2.openssl req -new -key server.key -out server.csr
生成Certificate Signing Request(CSR),生成的csr文件交給CA簽名后形成服務端自己的證書.屏幕上將有提示,依照其指示一步一步輸入要 求的個人信息即可.

3.對客戶端也作同樣的命令生成key及csr文件:
openssl genrsa -des3 -out client.key 1024
openssl req -new -key client.key -out client.csr

4.CSR文件必須有CA的簽名才可形成證書.可將此文件發送到verisign等地方由它驗證,要交一大筆錢,何不自己做CA呢.
首先生成CA的key文件:
openssl -des3 -out ca.key 1024
在生成CA自簽名的證書:
openssl req -new -x509 -key ca.key -out ca.crt
如果想讓此證書有個期限,如一年,則加上”-days 365”.
(“如果非要為這個證書加上一個期限,我情愿是..一萬年”)

5.用生成的CA的證書為剛才生成的server.csr,client.csr文件簽名:
可以用openssl中CA系列命令,但不是很好用(也不是多難,唉,一言難盡),一篇文章中推薦用mod_ssl中的sign.sh腳本,試了一下,確實方便了不 少,如果ca.csr存在的話,只需:
./sigh.sh server.csr
./sign.sh client.csr
相應的證書便生成了(后綴.crt).

現在我們所需的全部文件便生成了.

其實openssl中還附帶了一個叫CA.pl的文件(在安裝目錄中的misc子目錄下),可用其生成以上的文件,使用也比較方便,但此處就不作介紹了.

三、需要了解的一些函數:

1.int????SSL_CTX_set_cipher_list(SSL_CTX *,const char *str);

SSL_CTX_set_cipher_list() sets the list of available ciphers for?ctx?using the control string?str. The format of the string is described in?ciphers. The list of ciphers is inherited by all?ssl?objects created from?ctx.
根據SSL/TLS規范,在ClientHello中,客戶端會提交一份自己能夠支持的加密方法的列表,由服務端選擇一種方法后在ServerHello中通知客戶端, 從而完成加密算法的協商.

如果服務端只設置了一種加密套件,那么客戶端要么接受要么返回錯誤。加密套件的選擇是由服務端做出的。

可用的算法為:
EDH-RSA-DES-CBC3-SHA
EDH-DSS-DES-CBC3-SHA
DES-CBC3-SHA
DHE-DSS-RC4-SHA
IDEA-CBC-SHA
RC4-SHA
RC4-MD5
EXP1024-DHE-DSS-RC4-SHA
EXP1024-RC4-SHA
EXP1024-DHE-DSS-DES-CBC-SHA
EXP1024-DES-CBC-SHA
EXP1024-RC2-CBC-MD5
EXP1024-RC4-MD5
EDH-RSA-DES-CBC-SHA
EDH-DSS-DES-CBC-SHA
DES-CBC-SHA
EXP-EDH-RSA-DES-CBC-SHA
EXP-EDH-DSS-DES-CBC-SHA
EXP-DES-CBC-SHA
EXP-RC2-CBC-MD5
EXP-RC4-MD5


下面的openssl命令可以列出支持的所有算法,并按強度排序:

#?openssl?ciphers?-v?‘ALL:!ADH:@STRENGTH’
DHE-RSA-AES256-SHA??????SSLv3?Kx=DH???????Au=RSA??Enc=AES(256)??Mac=SHA1
DHE-DSS-AES256-SHA??????SSLv3?Kx=DH???????Au=DSS??Enc=AES(256)??Mac=SHA1
AES256-SHA??????????????SSLv3?Kx=RSA??????Au=RSA??Enc=AES(256)??Mac=SHA1
EDH-RSA-DES-CBC3-SHA????SSLv3?Kx=DH???????Au=RSA??Enc=3DES(168)?Mac=SHA1
EDH-DSS-DES-CBC3-SHA????SSLv3?Kx=DH???????Au=DSS??Enc=3DES(168)?Mac=SHA1
DES-CBC3-SHA????????????SSLv3?Kx=RSA??????Au=RSA??Enc=3DES(168)?Mac=SHA1
DES-CBC3-MD5????????????SSLv2?Kx=RSA??????Au=RSA??Enc=3DES(168)?Mac=MD5
DHE-RSA-AES128-SHA??????SSLv3?Kx=DH???????Au=RSA??Enc=AES(128)??Mac=SHA1
DHE-DSS-AES128-SHA??????SSLv3?Kx=DH???????Au=DSS??Enc=AES(128)??Mac=SHA1
AES128-SHA??????????????SSLv3?Kx=RSA??????Au=RSA??Enc=AES(128)??Mac=SHA1
DHE-DSS-RC4-SHA?????????SSLv3?Kx=DH???????Au=DSS??Enc=RC4(128)??Mac=SHA1
RC4-SHA?????????????????SSLv3?Kx=RSA??????Au=RSA??Enc=RC4(128)??Mac=SHA1
RC4-MD5?????????????????SSLv3?Kx=RSA??????Au=RSA??Enc=RC4(128)??Mac=MD5
RC2-CBC-MD5?????????????SSLv2?Kx=RSA??????Au=RSA??Enc=RC2(128)??Mac=MD5
RC4-MD5?????????????????SSLv2?Kx=RSA??????Au=RSA??Enc=RC4(128)??Mac=MD5
RC4-64-MD5??????????????SSLv2?Kx=RSA??????Au=RSA??Enc=RC4(64)???Mac=MD5
EXP1024-DHE-DSS-RC4-SHA?SSLv3?Kx=DH(1024)?Au=DSS??Enc=RC4(56)???Mac=SHA1?export
EXP1024-RC4-SHA?????????SSLv3?Kx=RSA(1024)?Au=RSA??Enc=RC4(56)???Mac=SHA1?export
EXP1024-DHE-DSS-DES-CBC-SHA?SSLv3?Kx=DH(1024)?Au=DSS??Enc=DES(56)???Mac=SHA1?export
EXP1024-DES-CBC-SHA?????SSLv3?Kx=RSA(1024)?Au=RSA??Enc=DES(56)???Mac=SHA1?export
EXP1024-RC2-CBC-MD5?????SSLv3?Kx=RSA(1024)?Au=RSA??Enc=RC2(56)???Mac=MD5??export
EXP1024-RC4-MD5?????????SSLv3?Kx=RSA(1024)?Au=RSA??Enc=RC4(56)???Mac=MD5??export
EDH-RSA-DES-CBC-SHA?????SSLv3?Kx=DH???????Au=RSA??Enc=DES(56)???Mac=SHA1
EDH-DSS-DES-CBC-SHA?????SSLv3?Kx=DH???????Au=DSS??Enc=DES(56)???Mac=SHA1
DES-CBC-SHA?????????????SSLv3?Kx=RSA??????Au=RSA??Enc=DES(56)???Mac=SHA1
DES-CBC-MD5?????????????SSLv2?Kx=RSA??????Au=RSA??Enc=DES(56)???Mac=MD5
EXP-EDH-RSA-DES-CBC-SHA?SSLv3?Kx=DH(512)??Au=RSA??Enc=DES(40)???Mac=SHA1?export
EXP-EDH-DSS-DES-CBC-SHA?SSLv3?Kx=DH(512)??Au=DSS??Enc=DES(40)???Mac=SHA1?export
EXP-DES-CBC-SHA?????????SSLv3?Kx=RSA(512)?Au=RSA??Enc=DES(40)???Mac=SHA1?export
EXP-RC2-CBC-MD5?????????SSLv3?Kx=RSA(512)?Au=RSA??Enc=RC2(40)???Mac=MD5??export
EXP-RC4-MD5?????????????SSLv3?Kx=RSA(512)?Au=RSA??Enc=RC4(40)???Mac=MD5??export
EXP-RC2-CBC-MD5?????????SSLv2?Kx=RSA(512)?Au=RSA??Enc=RC2(40)???Mac=MD5??export
EXP-RC4-MD5?????????????SSLv2?Kx=RSA(512)?Au=RSA??Enc=RC4(40)???Mac=MD5??export


這些算法按一定優先級排列,如果不作任何指定,將選用DES-CBC3-SHA.用SSL_CTX_set_cipher_list可以指定自己希望用的算法(實際上只是 提高其優先級,是否能使用還要看對方是否支持).

我們在程序中選用了RC4做加密,MD5做消息摘要(先進行MD5運算,后進行RC4加密).即
SSL_CTX_set_cipher_list(ctx,”RC4-MD5”);

在消息傳輸過程中采用對稱加密(比公鑰加密在速度上有極大的提高),其所用秘鑰(shared secret)在握手過程中中協商(每次對話過程均不同, 在一次對話中都有可能有幾次改變),并通過公鑰加密的手段由客戶端提交服務端.


SSL_set_cipher_list

int SSL_set_cipher_list(SSL *ssl, const char *str);

SSL_set_cipher_list() sets the list of ciphers only for?ssl.

RETURN VALUES

SSL_CTX_set_cipher_list() and SSL_set_cipher_list() return 1 if any cipher could be selected and 0 on complete failure.


2.void SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int (*callback)(int, X509_STORE_CTX *));
缺省mode是SSL_VERIFY_NONE,如果想要驗證對方的話,便要將此項變成SSL_VERIFY_PEER.SSL/TLS中缺省只驗證server,如果沒有設置 SSL_VERIFY_PEER的話,客戶端連證書都不會發過來.

3.int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile,const char *CApath);
要驗證對方的話,當然裝要有CA的證書了,此函數用來便是加載CA的證書文件的.

4.int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type);
加載自己的證書文件.

5.int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);
加載自己的私鑰,以用于簽名.

6.int SSL_CTX_check_private_key(SSL_CTX *ctx);
調用了以上兩個函數后,自己檢驗一下證書與私鑰是否配對.
7.void RAND_seed(const void *buf,int num);
在win32的環境中client程序運行時出錯(SSL_connect返回-1)的一個主要機制便是與UNIX平臺下的隨機數生成機制不同(握手的時候用的到).具體描述可見mod_ssl的FAQ.解決辦法就是調用此函數,其中buf應該為一隨機的字符串,作為”seed”.
還可以采用一下兩個函數:
void RAND_screen(void);
int RAND_event(UINT, WPARAM, LPARAM);
其中RAND_screen()以屏幕內容作為”seed”產生隨機數,RAND_event可以捕獲windows中的事件(event),以此為基礎產生隨機數.如果一直有 用戶干預的話,用這種辦法產生的隨機數能夠”更加隨機”,但如果機器一直沒人理(如總停在登錄畫面),則每次都將產生同樣的數字.

這幾個函數都只在WIN32環境下編譯時有用,各種UNIX下就不必調了.
大量其他的相關函數原型,見crypto\rand\rand.h.

8.OpenSSL_add_ssl_algorithms()或SSLeay_add_ssl_algorithms()
其實都是調用int SSL_library_init(void)
進行一些必要的初始化工作,用openssl編寫SSL/TLS程序的話第一句便應是它.

9.void????SSL_load_error_strings(void );
如果想打印出一些方便閱讀的調試信息的話,便要在一開始調用此函數.

10.void ERR_print_errors_fp(FILE *fp);
如果調用了SSL_load_error_strings()后,便可以隨時用ERR_print_errors_fp()來打印錯誤信息了.

11.X509 *SSL_get_peer_certificate(SSL *s);
握手完成后,便可以用此函數從SSL結構中提取出對方的證書(此時證書得到且已經驗證過了)整理成X509結構.

12.X509_NAME *X509_get_subject_name(X509 *a);
得到證書所有者的名字,參數可用通過SSL_get_peer_certificate()得到的X509對象.

13.X509_NAME *X509_get_issuer_name(X509 *a)
得到證書簽署者(往往是CA)的名字,參數可用通過SSL_get_peer_certificate()得到的X509對象.

14.char *X509_NAME_oneline(X509_NAME *a,char *buf,int size);
將以上兩個函數得到的對象變成字符型,以便打印出來.

15.SSL_METHOD的構造函數,包括
SSL_METHOD *TLSv1_server_method(void);????/* TLSv1.0 */
SSL_METHOD *TLSv1_client_method(void);????/* TLSv1.0 */

SSL_METHOD *SSLv2_server_method(void);????/* SSLv2 */
SSL_METHOD *SSLv2_client_method(void);????/* SSLv2 */

SSL_METHOD *SSLv3_server_method(void);????/* SSLv3 */
SSL_METHOD *SSLv3_client_method(void);????/* SSLv3 */

SSL_METHOD *SSLv23_server_method(void);????/* SSLv3 but can rollback to v2 */
SSL_METHOD *SSLv23_client_method(void);????/* SSLv3 but can rollback to v2 */
在程序中究竟采用哪一種協議(TLSv1/SSLv2/SSLv3),就看調哪一組構造函數了.



四:程序源代碼:


服務器端:

  • #include <stdio.h>
  • #include <stdlib.h>
  • #include <errno.h>
  • #include <string.h>
  • #include <sys/types.h>
  • #include <netinet/in.h>
  • #include <sys/socket.h>
  • #include <sys/wait.h>
  • #include <unistd.h>
  • #include <arpa/inet.h>
  • #include <openssl/ssl.h>
  • #include <openssl/err.h>
  • #define MAXBUF 1024
  • /************關于本文檔********************************************
  • *filename: ssl-server.c
  • *purpose: 演示利用 OpenSSL 庫進行基于 IP層的 SSL 加密通訊的方法,這是服務器端例子
  • *wrote by: zhoulifa(zhoulifa@163.com) 周立發(http://zhoulifa.bokee.com)
  • Linux愛好者 Linux知識傳播者 SOHO族 開發者 最擅長C語言
  • *date time:2007-02-02 19:40
  • *Note: 任何人可以任意復制代碼并運用這些文檔,當然包括你的商業用途
  • * 但請遵循GPL
  • *Thanks to:Google
  • *Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
  • * 科技站在巨人的肩膀上進步更快!感謝有開源前輩的貢獻!
  • *********************************************************************/
  • int main(int argc, char **argv)
  • {
  • int sockfd, new_fd;
  • socklen_t len;
  • struct sockaddr_in my_addr, their_addr;
  • unsigned int myport, lisnum;
  • char buf[MAXBUF + 1];
  • SSL_CTX *ctx;
  • if (argv[1])
  • myport = atoi(argv[1]);
  • else
  • myport = 7838;
  • if (argv[2])
  • lisnum = atoi(argv[2]);
  • else
  • lisnum = 2;
  • /* SSL 庫初始化 */
  • SSL_library_init();
  • /* 載入所有 SSL 算法 */
  • OpenSSL_add_all_algorithms();
  • /* 載入所有 SSL 錯誤消息 */
  • SSL_load_error_strings();
  • /* 以 SSL V2 和 V3 標準兼容方式產生一個 SSL_CTX ,即 SSL Content Text */
  • ctx = SSL_CTX_new(SSLv23_server_method());
  • /* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 單獨表示 V2 或 V3標準 */
  • if (ctx == NULL) {
  • ERR_print_errors_fp(stdout);
  • exit(1);
  • }
  • /* 載入用戶的數字證書, 此證書用來發送給客戶端。 證書里包含有公鑰 */
  • if (SSL_CTX_use_certificate_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) {
  • ERR_print_errors_fp(stdout);
  • exit(1);
  • }
  • /* 載入用戶私鑰 */
  • if (SSL_CTX_use_PrivateKey_file(ctx, argv[5], SSL_FILETYPE_PEM) <= 0) {
  • ERR_print_errors_fp(stdout);
  • exit(1);
  • }
  • /* 檢查用戶私鑰是否正確 */
  • if (!SSL_CTX_check_private_key(ctx)) {
  • ERR_print_errors_fp(stdout);
  • exit(1);
  • }
  • /* 開啟一個 socket 監聽 */
  • if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
  • perror("socket");
  • exit(1);
  • } else
  • printf("socket created\n");
  • bzero(&my_addr, sizeof(my_addr));
  • my_addr.sin_family = PF_INET;
  • my_addr.sin_port = htons(myport);
  • if (argv[3])
  • my_addr.sin_addr.s_addr = inet_addr(argv[3]);
  • else
  • my_addr.sin_addr.s_addr = INADDR_ANY;
  • if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
  • == -1) {
  • perror("bind");
  • exit(1);
  • } else
  • printf("binded\n");
  • if (listen(sockfd, lisnum) == -1) {
  • perror("listen");
  • exit(1);
  • } else
  • printf("begin listen\n");
  • while (1) {
  • SSL *ssl;
  • len = sizeof(struct sockaddr);
  • /* 等待客戶端連上來 */
  • if ((new_fd =
  • accept(sockfd, (struct sockaddr *) &their_addr,
  • &len)) == -1) {
  • perror("accept");
  • exit(errno);
  • } else
  • printf("server: got connection from %s, port %d, socket %d\n",
  • inet_ntoa(their_addr.sin_addr),
  • ntohs(their_addr.sin_port), new_fd);
  • /* 基于 ctx 產生一個新的 SSL */
  • ssl = SSL_new(ctx);
  • /* 將連接用戶的 socket 加入到 SSL */
  • SSL_set_fd(ssl, new_fd);
  • /* 建立 SSL 連接 */
  • if (SSL_accept(ssl) == -1) {
  • perror("accept");
  • close(new_fd);
  • break;
  • }
  • /* 開始處理每個新連接上的數據收發 */
  • bzero(buf, MAXBUF + 1);
  • strcpy(buf, "server->client");
  • /* 發消息給客戶端 */
  • len = SSL_write(ssl, buf, strlen(buf));
  • if (len <= 0) {
  • printf
  • ("消息'%s'發送失敗!錯誤代碼是%d,錯誤信息是'%s'\n",
  • buf, errno, strerror(errno));
  • goto finish;
  • } else
  • printf("消息'%s'發送成功,共發送了%d個字節!\n",
  • buf, len);
  • bzero(buf, MAXBUF + 1);
  • /* 接收客戶端的消息 */
  • len = SSL_read(ssl, buf, MAXBUF);
  • if (len > 0)
  • printf("接收消息成功:'%s',共%d個字節的數據\n",
  • buf, len);
  • else
  • printf
  • ("消息接收失敗!錯誤代碼是%d,錯誤信息是'%s'\n",
  • errno, strerror(errno));
  • /* 處理每個新連接上的數據收發結束 */
  • finish:
  • /* 關閉 SSL 連接 */
  • SSL_shutdown(ssl);
  • /* 釋放 SSL */
  • SSL_free(ssl);
  • /* 關閉 socket */
  • close(new_fd);
  • }
  • /* 關閉監聽的 socket */
  • close(sockfd);
  • /* 釋放 CTX */
  • SSL_CTX_free(ctx);
  • return 0;
  • }
  • 客戶端:

  • #include <stdio.h>
  • #include <string.h>
  • #include <errno.h>
  • #include <sys/socket.h>
  • #include <resolv.h>
  • #include <stdlib.h>
  • #include <netinet/in.h>
  • #include <arpa/inet.h>
  • #include <unistd.h>
  • #include <openssl/ssl.h>
  • #include <openssl/err.h>
  • #define MAXBUF 1024
  • void ShowCerts(SSL * ssl)
  • {
  • X509 cert;
  • char *line;
  • cert = SSL_get_peer_certificate(ssl);
  • if (cert != NULL) {
  • printf("數字證書信息:\n");
  • line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
  • printf("證書: %s\n", line);
  • free(line);
  • line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
  • printf("頒發者: %s\n", line);
  • free(line);
  • X509_free(cert);
  • } else
  • printf("無證書信息!\n");
  • }
  • /***關于本文檔**********************************
  • filename: ssl-client.c
  • *purpose: 演示利用 OpenSSL 庫進行基于 IP層的 SSL 加密通訊的方法,這是客戶端例子
  • *wrote by: zhoulifa(zhoulifa@163.com) 周立發(http://zhoulifa.bokee.com)
  • Linux愛好者 Linux知識傳播者 SOHO族 開發者 最擅長C語言
  • *date time:2007-02-02 20:10
  • *Note: 任何人可以任意復制代碼并運用這些文檔,當然包括你的商業用途
  • 但請遵循GPL
  • Thanks to:Google
  • *Hope:希望越來越多的人貢獻自己的力量,為科學技術發展出力
  • 科技站在巨人的肩膀上進步更快!感謝有開源前輩的貢獻!
  • ***********************************************************/
  • int main(int argc, char *argv)
  • {
  • int sockfd, len;
  • struct sockaddr_in dest;
  • char buffer[MAXBUF + 1];
  • SSL_CTX *ctx;
  • SSL *ssl;
  • if (argc != 3) {
  • printf
  • ("參數格式錯誤!正確用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用來從某個 IP 地址的服務器某個端口接收最多 MAXBUF 個字節的消息",
  • argv[0], argv[0]);
  • exit(0);
  • }
  • / SSL 庫初始化,參看 ssl-server.c 代碼 /
  • SSL_library_init();
  • OpenSSL_add_all_algorithms();
  • SSL_load_error_strings();
  • ctx = SSL_CTX_new(SSLv23_client_method());
  • if (ctx == NULL) {
  • ERR_print_errors_fp(stdout);
  • exit(1);
  • }
  • / 創建一個 socket 用于 tcp 通信 /
  • if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  • perror("Socket");
  • exit(errno);
  • }
  • printf("socket created\n");
  • / 初始化服務器端(對方)的地址和端口信息 /
  • bzero(&dest, sizeof(dest));
  • dest.sin_family = AF_INET;
  • dest.sin_port = htons(atoi(argv[2]));
  • if (inet_aton(argv[1], (struct in_addr ) &dest.sin_addr.s_addr) == 0) {
  • perror(argv[1]);
  • exit(errno);
  • }
  • printf("address created\n");
  • /* 連接服務器 /
  • if (connect(sockfd, (struct sockaddr ) &dest, sizeof(dest)) != 0) {
  • perror("Connect ");
  • exit(errno);
  • }
  • printf("server connected\n");
  • /* 基于 ctx 產生一個新的 SSL /
  • ssl = SSL_new(ctx);
  • SSL_set_fd(ssl, sockfd);
  • / 建立 SSL 連接 /
  • if (SSL_connect(ssl) == -1)
  • ERR_print_errors_fp(stderr);
  • else {
  • printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
  • ShowCerts(ssl);
  • }
  • / 接收對方發過來的消息,最多接收 MAXBUF 個字節 /
  • bzero(buffer, MAXBUF + 1);
  • / 接收服務器來的消息 /
  • len = SSL_read(ssl, buffer, MAXBUF);
  • if (len > 0)
  • printf("接收消息成功:'%s',共%d個字節的數據\n",
  • buffer, len);
  • else {
  • printf
  • ("消息接收失敗!錯誤代碼是%d,錯誤信息是'%s'\n",
  • errno, strerror(errno));
  • goto finish;
  • }
  • bzero(buffer, MAXBUF + 1);
  • strcpy(buffer, "from client->server");
  • / 發消息給服務器 /
  • len = SSL_write(ssl, buffer, strlen(buffer));
  • if (len < 0)
  • printf
  • ("消息'%s'發送失敗!錯誤代碼是%d,錯誤信息是'%s'\n",
  • buffer, errno, strerror(errno));
  • else
  • printf("消息'%s'發送成功,共發送了%d個字節!\n",
  • buffer, len);
  • finish:
  • / 關閉連接 */
  • SSL_shutdown(ssl);
  • SSL_free(ssl);
  • close(sockfd);
  • SSL_CTX_free(ctx);
  • return 0;
  • }

  • 編譯程序用下列命令:
    gcc -Wall ssl-client.c -o client -L /usr/local/openssl/lib/ -Wl,-R /usr/local/openssl/lib/ -lssl -lcrypto
    gcc -Wall ssl-server.c -o server -L /usr/local/openssl/lib/?-Wl,-R /usr/local/openssl/lib/ -lssl -lcrypto
    運行程序用如下命令:
    ./server 7838 1 127.0.0.1 cacert.pem privkey.pem
    ./client 127.0.0.1 7838

    用下面這兩個命令產生上述cacert.pem和privkey.pem文件:
    openssl genrsa -out privkey.pem 2048
    openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095

    總結

    以上是生活随笔為你收集整理的用OpenSSL编写SSL,TLS程序的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。