1. 程式人生 > >基於SSL協議的安全網路通訊程式

基於SSL協議的安全網路通訊程式

 基於SSL協議的安全網路通訊程式

****************************************

目錄結構:

1. SSL理解
1.1 SSL的優點
1.2 SSL密文會話的安全機制
1.3 SSL工作過程
(1) SSL分層結構
(2) SSL握手過程
(3) 利用Wireshark分析SSL工作過程
2. X.509證書相關檔案的生成
3. 實現SSL伺服器和客戶端

****************************************

1. SSL理解

1.1 SSL的優點

(1)提供較高的安全性保證。SSL利用資料加密、身份驗證和訊息完整性驗證機制,保證資料傳輸的安全性。

(2)支援各種應用層協議。雖然SSL設計的初衷是為了解決Web安全性問題,但是由於SSL位於應用層和傳輸層之間,它可以為任何基於TCP等可靠連線的應用層協議提供安全性保證

(3)部署簡單。目前SSL己經成為網路中用來鑑別網站和網頁瀏覽者身份,在客戶端瀏覽器和Web伺服器之間進行密文資料通訊的全球化標準。SSL協議已被整合到大部分的瀏覽器中,如IE、Netscape、Firefox等。這就意味著幾乎任意一臺裝有瀏覽器的計算機都支援SSL連線,不需要安裝額外的客戶端軟體。

1.2 SSL密文會話的安全機制

(1)資料加密傳輸:資料需要加密後再進行傳輸,加密解密演算法為對稱金鑰算法。

(2)身份驗證

:建立連結時需要對各通訊對端進行身份驗證,身份驗證利用證書和數字簽名技術,其中客戶端的驗證是可選的

(3)訊息完整性驗證:訊息傳輸過程中使用MAC演算法來檢驗訊息的完整性。

資料加密傳輸

與非對稱金鑰體制相比,對稱金鑰體制具有計算速度快的優點,通常用於對大量資料進行加密(如對整個資料流加密);而非對稱金鑰體制,一般用於數字簽名和對少量的資訊進行加密。SSL加密管道上的資料加解密使用對稱金鑰體制,目前主要支援的演算法有DES、 3DES、 AES等,這些演算法都可以有效地防止傳輸的資料被竊聽。

身份驗證

電子商務和網上銀行等Web應用中必須保證客戶要登入的Web伺服器是真實的,以免重要資訊被非法竊取。SSL安全協議利用數字簽名來進行通訊對端的身份驗證。非對稱金鑰體制可以用來實現數字簽名。由於通過私鑰加密後的資料只能利用對應的公鑰進行解密

,因此根據解密是否成功,就可以判斷髮送者的身份,如同傳送者對資料進行了 "簽名"

SSL客戶端必須驗證SSL伺服器的身份,SSL伺服器是否驗證SSL客戶端的身份,則由SSL伺服器決定。使用數字簽名驗證身份時,需要確保被驗證端的公鑰是真實的,否則,非法使用者可能會冒充被驗證端與驗證端通訊。

訊息完整性驗證

為了避免網路中傳輸的資料被非法篡改,SSL利用基於MD5SHAMAC(訊息認證碼)演算法來保證訊息的完整性。

MAC演算法要求通訊雙方具有相同的金鑰,否則MAC值附件驗證將會失敗。因此,利用MAC演算法驗證訊息完整性之前,需要在資料通訊的雙方部署相同的金鑰

利用非對稱金鑰演算法保證金鑰本身的安全

SSL利用非對稱金鑰體制加密金鑰的方法來實現金鑰交換,保證非授權的第三方無法獲取相應的金鑰。

利用非對稱金鑰演算法加密對稱金鑰之前,Alice需要獲取Bob的公鑰,並保證該公鑰確實屬於Bob,否則,金鑰可能會被未授權的非法使用者竊取。SSL利用PKI提供的機制保證公鑰的真實性

PKI保證公鑰的真實性

數字證書(簡稱證書)是一個包含使用者的公鑰及其身份資訊的檔案,證明了客戶與公鑰的關聯。數字證書由權威機構(CA)簽發,並由CA保證數字證書的真實性。

驗證SSL伺服器/SSL客戶端的身份之前,SSL伺服器和SSL客戶端需要將從CA獲取的證書傳送給通訊對端,對端通過PKI判斷該證書的真實性。如果該證書確實屬於SSL伺服器和SSL客戶端,則通訊對端利用該證書中的公鑰驗證SSL伺服器和SSL客戶端的身份。

1.3 SSL工作過程

(1) SSL分層結構

 



(2) SSL握手過程

SSL握手協議是SSL協議中最複雜的協議。SSL通過握手過程在客戶端和伺服器之間協商會話引數,並建立會話。會話包含的主要引數有會話ID、對方的證書、加密套件(金鑰交換演算法、資料加密演算法和MAC演算法等)以及主金鑰(mastersecret)通過SSL會話傳輸的資料,都將採用此次會話的主金鑰和加密套件進行加密、計算MAC等。

SSL握手協議由一系列報文組成,根據功能基本上可以分為四個階段:

1)      第一階段是建立安全能力。

2)      第二階段是伺服器鑑別和金鑰交換。

3)      第三個階段是客戶鑑別和金鑰交換。

4)      第四個階段是完成握手階段。

(3)利用Wireshark分析SSL工作過程

1.handshake--client hello

註釋:

ECC演算法和DH結合使用,用於金鑰磋商,這個金鑰交換演算法稱為ECDH

使用RC4加密體制演算法對通訊資料加密(金鑰長度128位)

使用SHA雜湊演算法進行訊息完整性驗證

使用RSA公鑰體制演算法進行證書驗證和對稱金鑰交換

【既然金鑰交換演算法有很多種(RSA 和DH)那SSL握手期間用哪種呢,這個就是之前由選擇的ciphersuit決定的,比如選擇的是SSL_RSA_WITH_RC4_128_MD5 = 0x0004,那就是RSA的金鑰交換演算法即用非對稱加密對稱將金鑰傳送到對方,若選擇的是SSL_DHE_RSA_EXPORT….那就使用DH交換演算法】

2.handshake--server hello

3.S—>C handshake—certificate 伺服器端向客戶端傳送伺服器證書


【Certificate(可選):伺服器發一個證書或一個證書鏈到客戶端,證書鏈開始於伺服器公共鑰匙並結束於證明權威的根證書。該證書用於向客戶端確認伺服器的身份,該訊息是可選的。如果配置伺服器的SSL需要驗證伺服器的身份,會發送該訊息。多數電子商務應用都需要伺服器端身份驗證。】

4.S-->Chandshake—sever key exchange   handshake—sever hello done 


伺服器傳送公鑰和簽名信息

【如果伺服器傳送的公共金鑰對加密金鑰的交換不是很合適,則傳送一個伺服器金鑰交換訊息。即和客戶端協商金鑰。

RSA方式金鑰交換訊息則把訊息中的加密用公鑰放入會話快取中,作為客戶端這邊握手階段的寫金鑰而不是用伺服器證書中的公鑰。

DH 方式的訊息就把訊息中的 p,g,Ys三個引數ECDH金鑰交換情況下是G和A)記錄下來,有這些 client端就可以計算出 pre-master了,只要回頭再把自己這邊的Yc引數發過去, server端就也能計算出相同的 pre-maseter了。

為了防止訊息被惡意篡改,Server Key exchange訊息中還要包含一個對金鑰引數(pubKey簽名(Signature)

(1)serverkey exchange Pubkey  signature

(2)Server hello done

5.C->S  handshake—clientkey exchange


客戶端產生一個會話金鑰與伺服器共享。在SSL握手協議完成後,客戶端與伺服器端通訊資訊的加密就會使用該會話金鑰。如果使用RSA加密演算法客戶端將使用伺服器的公鑰將會話金鑰之後再發送給伺服器。伺服器使用自己的私鑰對接收的訊息進行解密得到共享的會話金鑰

若是 RSA 方式金鑰交換,則產生一個 48 位隨機數作為 pre-master 並用伺服器公鑰加密後發出去若是 DH 方式的金鑰交換,則根據 sever 的 g,p,Ys ,產生 Xa 和 Yc , Xa 和 Ys 能計算出 pre-master ,把產生的 Yc 放入訊息中發給server ,這樣 server 用它的 Xb 和 Yc 也能計算出 pre-master 了。計算出預主密碼後就順便把主密碼 (master secret) 給算出來了。算出主密碼就把對稱金鑰產生出來了(預主要密碼à主密碼à對稱金鑰)

clientkey exchange, change cipher spec ,encrypted handshakemessage

【Change cipher spec:客戶端要求伺服器在後續的通訊中使用加密模式

6.S->C handshake—new session ticket

New session ticket,change cipher spec,encrypted handshake message

【Change cipher spec:伺服器要求客戶端在後續的通訊中使用加密模式


“session Ticket”(RFC 5077)取代機制被引入,目標是消除伺服器需要維護每個客戶端的會話狀態快取的要求。相反,如果客戶指示它支援Session Ticket,在TLS握手的最後一步中伺服器將包含一個“NewSession Ticket”資訊,包含了一個加密通訊所需要的資訊,這些資料採用一個只有伺服器知道的金鑰進行加密。

以上1-6步,握手完成。下面開始客戶端和伺服器端開始傳輸加密資訊。

7.C-->傳送正式資料

【淘寶網登陸頁面SSL連線建立分析】

42.156.196.14 https://login.taobao.com/member/login.jhtml



(1)C-->S  client hello


(2)S-->C server hello

     Ciphersuite:TLS_RSA_WITH_RC4_128_SHA

(3)S-->C certificate 證書鏈

(4)C-->S Client Key Exchange

    RSA Encrypted Secret

(5)C-->S New Session Ticket

2. X.509證書相關檔案的生成

生成數字證書相關檔案的步驟:

1.生成伺服器端的私鑰(key檔案),執行命令

openssl genrsa -des3 -out server.key1024   

Enter pass phrase for server.key:123456

2.生成伺服器端的csr檔案

openssl req -new -key server.key -outserver.csr -config openssl.cnf 

===拷貝 openssl\apps 下的openssl.cnf文件到out32dll目錄下,就可以使用 Openssl了。

Enter pass phrase for server.key:123456

A challenge password:147258

3.客戶端生成key檔案

openssl genrsa -des3 -out client.key1024    

Enter pass phrase for client.key:123456

4.生成客戶端的csr檔案

openssl req -new -key client.key -outclient.csr -config openssl.cnf

Enter pass phrase for client.key:123456

A challenge password:147258

5.生成自己的CA

 openssl req -new -x509 -keyout ca.key -outca.crt -config openssl.cnf

Enter PEM pass phrase:123456

生成ca.key和ca.crt證書檔案

6.用生成的ca給伺服器的csr檔案簽名,生成伺服器端的證書

openssl ca -in server.csr -out server.crt-cert ca.crt -keyfile ca.key -config openssl.cnf

===將apps目錄下的demoCA目錄以及根目錄下的crypto目錄複製到out32dll目錄下;並在demoCA目錄下新建資料夾newcerts.

Enter pass phrase for ca.key:123456

7.用生成的ca給客戶端的csr檔案簽名,生成客戶端的證書

openssl ca -in client.csr -out client.crt-cert ca.crt -keyfile ca.key -config openssl.cnf

===清空index.txt檔案內容後正常生成

Enter pass phrase for ca.key:123456

生成的相關檔案如下圖所示:

3. 實現SSL伺服器和客戶端

對於上面的證書生成過程中,SSL伺服器和客戶端中所需要使用的只有五個檔案,分別是ca.crt,client.crt,client.key,server.crt和server.key。

客戶端需要ca.crt,client.crt,client.key這三個檔案,

伺服器端需要ca.crt,server.crt,server.key這三個檔案。

原始碼:

 SSL Server端:

//
#include "openssl/rsa.h" 
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
#include <iostream>

using namespace std;

#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")

/*所有需要的引數資訊都在此處以#define的形式提供*/

#define CERTF "D:\\openssl-1.0.1e\\out32dll\\server.crt" /*客戶端的證書(需經CA簽名)*/
#define KEYF "D:\\openssl-1.0.1e\\out32dll\\server.key" /*客戶端的私鑰(建議加密儲存)*/
#define CACERT "D:\\openssl-1.0.1e\\out32dll\\ca.crt" /*CA 的證書*/
#define PORT 7758 /*服務端的埠*/

#define CHK_NULL(x) if ((x)==NULL) { printf("null\n"); }
#define MAXLEN 4096	

int main ()
{
	int err;

	int ListenSock;//監聽套接字
	int ConnectSock;//連線套接字
	
	SSL_CTX* ctx;
	SSL* ssl;
	X509* client_cert;
//	char* str;
	char buf [MAXLEN] = {0};
//	char szMsg[4096] = {0}; 
	//SSL_METHOD *meth;

	WSADATA wsaData;
	system("title SSL_SERVER");
	system("color 0a");
	if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
		printf("WSAStartup()fail:%d\n",GetLastError());
		return -1;
	} 
	

	OpenSSL_add_ssl_algorithms(); /*初始化*/
	SSL_load_error_strings(); /*為列印除錯資訊作準備*/

	//注意這裡是server和和客戶端不同
	//meth=TLSv1_server_method();
	ctx = SSL_CTX_new (TLSv1_server_method()); //採用什麼協議(SSLv2/SSLv3/TLSv1)在此指定
	CHK_NULL(ctx);

	SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /*驗證與否*/
	SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若驗證,則放置CA證書*/


	if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) 
	{
		cout<<"伺服器端證書檢查失敗!"<<endl;
		exit(0);
	}

	if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) 
	{
		cout <<"伺服器端key檢查失敗!"<<endl;
		system("pause");
		exit(0);
	}
	else
	{
		cout<<"伺服器端key檢查成功!"<<endl;
	}

	if (!SSL_CTX_check_private_key(ctx)) 
	{
		cout << "伺服器端證書和key不匹配!"<<endl;
		exit(0);
	}
	 SSL_CTX_set_cipher_list(ctx,"AES128-SHA");//*********加密方式  1.4******
	 SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); //不需要任何重試請求
	

	//建立監聽套接字
	 ListenSock = socket (AF_INET, SOCK_STREAM, 0); 
	 if(ListenSock == INVALID_SOCKET)
	 {
		 cout << "監聽套接字建立失敗" << endl;
		 exit(0);
	 }
	 
	 //建立地址
	 sockaddr_in sin = {0};
	 sin.sin_family = AF_INET;
	 sin.sin_addr.s_addr = INADDR_ANY;
	 sin.sin_port = htons (PORT); //Servert Port Number

	 //套接字繫結
	 err = bind(ListenSock, (struct sockaddr*)&sin,sizeof(sin)); 
	 if(err == -1)
	 {
		 cout << "監聽套接字繫結失敗" << endl;
		 exit(0);
	 }
	 
	 /*接受TCP連結*/
	 err = listen (ListenSock, 5); //等待連線的佇列的最大長度為5
	 if(err == -1)
	 {
		 cout << "監聽套接字開啟監聽失敗" << endl;
		 exit(0);
	 }
	 
	 //接受客戶端連線
	 struct sockaddr_in sa_client;
	 int len=sizeof(struct sockaddr);
	 ConnectSock = accept(ListenSock, (struct sockaddr *)&sa_client, &len);
	 if(err == -1)
	 {
		 cout << "監聽套接字accept失敗" << endl;
		 exit(0);
	 }

	 cout<<"Connection from "<<inet_ntoa(sa_client.sin_addr)<<",port:"<<ntohs(sa_client.sin_port)<<"............"<<endl;
	 closesocket (ListenSock);    //**********
	 

	 /*--------------------TCP連線已建立,進行服務端的SSL過程.--------------- */
	 cout << "TCP連線建立,建立SSL連線中....\n" << endl;
	 //從初始化的ctx新建SSL
	 ssl = SSL_new (ctx); 
	 if(ssl == NULL)
	 {
		 cout << "SSL建立失敗" << endl;
		 exit(0);
	 }
	 
	 //(連線)套接字和SSL繫結  //<------------key
	 SSL_set_fd (ssl, ConnectSock);
	 //    SSL連線建立
	 err = SSL_accept (ssl);//等待一個TLS / SSL客戶端啟動TLS / SSL握手,類似於socket中的accept。
	 if(err == -1)
	 {
		 cout << "建立SSL連線失敗" << endl;
		 exit(0);
	 }
	 else
	 {
		 cout << "建立SSL連線成功" << endl;
	 }
	 
	 /*列印所有加密演算法的資訊(可選)*/
	 cout << "SSL連線演算法資訊:" << SSL_get_cipher (ssl) << endl; 
	 /*得到客戶端的證書並列印些資訊(可選) */  
	 client_cert = SSL_get_peer_certificate (ssl);  
	 if (client_cert != NULL) {  
		 cout << "客戶端證書:" << endl;  
		 cout << "subject:" <<X509_NAME_oneline(X509_get_subject_name (client_cert), 0, 0)<< endl;  
		 cout << "issuer:" <<X509_NAME_oneline(X509_get_issuer_name (client_cert),0,0) << endl;  
		 // CHK_NULL(str);  
		 X509_free (client_cert);/*如不再需要,需將證書釋放 */  
	 }  
	 else cout << "客戶端沒有證書資訊!" << endl; //客戶端認證失敗 
	 

	 //SSL通訊,用SSL_write,SSL_read代替send和recv
	 while(true)
	 {	
		 //接收訊息
		 err = SSL_read (ssl, buf, sizeof(buf) - 1); 
		 if(err == -1)
		 {
			 cout << "SSL_read接收訊息失敗" << endl;
			 exit(0);
		 }
		 buf[err] = '\0';
		 cout << "【Client】:" << buf << endl;
		 
		 //傳送訊息
		 cout << "請輸入要傳送的訊息:";
		 gets(buf);//end with CRLF or EOF
		 err = SSL_write(ssl, buf, strlen(buf));
		 if(err == -1)
		 {
			 cout << "SSL_write傳送訊息失敗" << endl;
			 exit(0);
		 }
		 cout << "【Server】:" << buf << endl;
	 }
	 
	 //關閉套接字和ssl
	 SSL_shutdown (ssl);
	 shutdown (ConnectSock,2);
	 SSL_free (ssl);
	 SSL_CTX_free (ctx);
	 closesocket(ConnectSock);
	 return 0;
}

SSL Client 端:

//
#include "openssl/rsa.h" 
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
#include <iostream>
using namespace std;

#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")

/*所有需要的引數資訊都在此處以#define的形式提供*/

#define CERTF "D:\\openssl-1.0.1e\\out32dll\\client.crt" /*客戶端的證書(需經CA簽名)*/
#define KEYF "D:\\openssl-1.0.1e\\out32dll\\client.key" /*客戶端的私鑰(建議加密儲存)*/
#define CACERT "D:\\openssl-1.0.1e\\out32dll\\ca.crt" /*CA 的證書*/
#define PORT 7758 /*服務端的埠*/
#define CHK_NULL(x) if ((x)==NULL) { printf("null\n"); }
#define MAXLEN 4096

int main ()
{
	int err;
	int sd;

	struct sockaddr_in sa;
	SSL_CTX* ctx;
	SSL* ssl;
	X509* server_cert;
//	char* str;
	char buf [MAXLEN] = {0};
//	char szMsg[MAXLEN] = {0}; 
	//SSL_METHOD *meth;
	//int seed_int[100]; /*存放隨機序列*/

	WSADATA wsaData;

	if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
		printf("WSAStartup()fail:%d\n",GetLastError());
		return -1;
	} 
	system("title SSL_CLIENT");
	system("color 0a");
	OpenSSL_add_ssl_algorithms(); /*1.SSL初始化*/
	SSL_load_error_strings(); /*2.SSL錯誤資訊初始化 為列印除錯資訊作準備*/

	//注意這裡是client和和伺服器不同
	ctx = SSL_CTX_new (TLSv1_client_method()); //3.建立本次會話所使用的協議(TLSv1); 4.申請SSL會話的環境
	CHK_NULL(ctx);
	//SSL_VERIFY_PEER:希望驗證對方的證書
	SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /*驗證與否*/  //5.設定會話的握手方式
	SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若驗證,則放置CA證書*/  //6.並載入CA證書


	if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) //7.載入自己(客戶端)的整數
	{
		cout << "客戶端證書檢查失敗!" << endl;
		exit(0);
	}
	if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) //8.載入客戶端的私鑰
	{
		cout << "客戶端key檢查失敗!" << endl;
		system("pause");
		exit(0);
	}
	else
	{
		cout<<"客戶端證key檢查成功!"<<endl;
	}

	if (!SSL_CTX_check_private_key(ctx))//9.檢查自己的證書和私鑰是否匹配 
	{
		cout << "客戶端證書和key不匹配!" << endl;
		exit(0);
	}

	 //10.加密方式  
 //   SSL_CTX_set_cipher_list(ctx, "RC4-MD5"); 
	SSL_CTX_set_cipher_list(ctx, "AES128-SHA");

    //處理握手多次  
	//設定ssl的模式為SSL_MODE_AUTO_RETRY,使用這個選項進行設定,如果伺服器突然希望進行一次新的握手,那麼OpenSSL可以在後臺處理它。
    SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); 

	/*構建隨機數生成機制,WIN32平臺必需*/
/*	srand( (unsigned)time( NULL ) );
	for( int i = 0; i < 100;i++ ) seed_int[i] = rand();
	RAND_seed(seed_int, sizeof(seed_int));*/
	
	//11.建立TCP連線請求
	sd = socket (AF_INET, SOCK_STREAM, 0); 
	if(sd == INVALID_SOCKET)
	{
		cout << "套接字建立失敗!" << endl;
		exit(0);
	}
	memset(&sa,'\0', sizeof(sa));
	sa.sin_family = AF_INET;
	
	// 獲取本地地址資訊
//	unsigned int SrcIp;
/*	char szLocalName[MAXLEN]={0};
	gethostname(szLocalName,MAXLEN);
	hostent* pHost=gethostbyname(szLocalName);
	if(pHost!=NULL)
		memcpy(&(sa.sin_addr.s_addr),pHost->h_addr_list[0],pHost->h_length);
	else
		exit(0);*/

	sa.sin_addr.s_addr = inet_addr ("127.0.0.1"); 
//	sa.sin_addr.s_addr = inet_addr ("192.168.146.1"); 

	sa.sin_port = htons (PORT); /* Server Port number *///SERVER PORT

	//TCP連線  等待伺服器的響應
	cout<<"等待連線中..."<<endl;
	int try_count=0;
	while(1)
	{
		err = connect(sd, (struct sockaddr*) &sa,sizeof(sa));
		if(try_count>5)
		{
			cout<<"TCP 連線超時..."<<endl;
			system("pause");
			exit(1);
		}
		
		else
		{
			if(err == -1)
			{
				//	cout << "TCP連線失敗!" << endl;
				try_count++;
				continue;
			}
			else
			{
				cout << "TCP連線成功!" << endl;
				break;
			}
		}
	}

	 /* 12.TCP 連結已建立.開始 SSL 握手過程.......................... */  
	//SSL連線
	//新建SSL
	ssl = SSL_new (ctx); //利用SSL會話的環境ctx申請SSL
	if(ssl == NULL)
	{
		cout << "新建SSL失敗!" << endl;
		exit(0);
	}
	//13. 套接字sd和SSL繫結
	SSL_set_fd (ssl, sd);
	//14.SLL連線(SSL handshake)
	//設定等待伺服器響應的超時時間
	while(1)
	{
		err = SSL_connect (ssl);
		if(err == -1)
		{
			cout << "SSL連線失敗" << endl;
			continue;
		}
		
		else
		{
			cout << "SSL連線成功" << endl;
			break;
		}
	}

	//列印連線資訊
	cout << "SSL連線演算法資訊:" << SSL_get_cipher (ssl) << endl;
	/*得到服務端的證書並列印些資訊(可選) */
	server_cert = SSL_get_peer_certificate (ssl);//從SSL套接字中提取對方的證書資訊,這些資訊已經被SSL驗證過了
	if (server_cert != NULL) {
		cout << "伺服器證書:" << endl;
		cout << "subject:" << X509_NAME_oneline( X509_get_subject_name (server_cert), 0, 0)<< endl;//得到證書所用者的名字
		cout << "issuer:" << X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0) << endl;
	//	CHK_NULL(str);
		X509_free (server_cert);/*如不再需要,需將證書釋放 */
	}
	else cout << "伺服器沒有證書資訊!" << endl; //伺服器端認證失敗

	//SSL通訊,用SSL_write,SSL_read代替send和recv
	while(true)
	{	
		//傳送訊息 int SSL_write(SSL *ssl,const void *buf,int num);
		cout << "請輸入要傳送的訊息:";
		gets(buf);
		err = SSL_write(ssl, buf, strlen(buf));
		if(err == -1)
		{
			cout << "SSL_write傳送訊息失敗" << endl;
			exit(0);
		}
		cout << "【Client】:" << buf << endl;

		//接收訊息 int SSL_read(SSL *ssl,void *buf,int num);
		err = SSL_read (ssl, buf, sizeof(buf) - 1); 
		if(err == -1)
		{
			cout << "SSL_read接收訊息失敗" << endl;
			exit(0);
		}
		buf[err] = '\0';
		cout << "【Server】:" << buf << endl;
	}

	/* 收尾工作 */
	SSL_shutdown (ssl);//關閉SSL套接字
	//shutdown( SOCKET s, int how); 0關閉套接字的讀功能   1關閉套接字的寫功能   2關閉套接字的讀寫功能
	//請注意shutdown()函式並不關閉套介面,且套介面所佔有的資源將被一直保持到closesocket()呼叫。
	//shutdown (sd,2);//
	SSL_free (ssl);//釋放SSL套接字
	SSL_CTX_free (ctx);//釋放SSL會話環境
	closesocket(sd);//
	return 0;
}

執行結果示例: