對於openssl應用編程這方面的詳細文檔很少,我是通過認真分析openssl源碼包中的示例代碼來學習並結合man文檔來理解它的基本結構的。SSL通訊模型為標准的C/S結構,除了在TCP層之上進行傳輸之外,與一般的通訊沒有什麼明顯的區別。下面我對用SSL建立安全的TCP連接的流程作一簡單分析。
一、數字證書准備
通常情況下我們的服務端需要
服務器端的私鑰server.key文件
服務器端證書server.crt文件
對於雙向認證連接,要用到
客戶端的私鑰client.key文件
客戶器端證書client.crt文件
對於單向認證連接,不需要用到戶端證書文件
二、 程序結構
與ssl有關的頭文件都放在openssl目錄下,通常需要用到這些:
#include
#include
#include
#include
#include
#include
1、對OPENSSL的一些必要的初始化
SSL_load_error_strings(); /*進行錯誤信息的初始化,不是必須的, 如果要使用OpenSSL的出錯信息打印,就要調用它*/
SSLeay_add_ssl_algorithms(); /*加載SSL算法庫*/
/*還可使用以下兩種方法加載SSL算法庫
SSL_library_init(void);
OpenSSL_add_ssl_algorithms();
*/
創建SSL上下方環境
meth = SSLv23_server_method();
/*客戶端模式有:
SSL_METHOD* TLSv1_client_method(void);TLSv1.0協議
SSL_METHOD* SSLv2_client_method(void);SSLv2協議
SSL_METHOD* SSLv3_client_method(void);SSLv3協議
SSL_METHOD* SSLv23_client_method(void);SSLv2/v3協議
服務端模式有:
SSL_METHOD* TLSv1_server_method(void);
SSL_METHOD* SSLv2_server_method(void);
SSL_METHOD* SSLv3_server_method(void);
SSL_METHOD* SSLv23_server_method(void);
*/
ctx = SSL_CTX_new (meth); /*在應用中,客戶端和服務端必須為相同模式*/
ctx為返回的為當前SSL會話環境的指針,我們根據自己的需要對它進行設置:
void SSL_CTX_set_verify(SSL_CTX*, int, int* (int, X509_STORE_CTX*));
設置證書驗證的方式。第一個參數是當前的CTX指針,第二個是驗證方式,如果是要驗證對方的話,就使用SSL_VERIFY_PEER。不需要的話,使用 SSL_VERIFY_NONE.一般情況下,客戶端需要驗證對方,而服務器不需要。第三個參數是處理驗證的回調函數,如果沒有特殊的需要,使用空指針就可以了。
void SSL_CTX_load_verify_locations(SSL_CTX*, const char*, const char*);
加載證書,第一個參數同上,參數二是證書文件的名稱,參數三是證書文件的路徑;
int SSL_CTX_use_certificate_file(SSL_CTX* ctx, const char* file,int type);
加載本地的證書;type指明證書文件的結構類型;失敗返回-1
SSL_CTX_set_default_passwd_cb_userdata(ctx, "123456");
設置SSL要加載的證書的口令,如果不設置的話加載證書時會出提示符要求輸入口令的,這樣在程序中使用就比較麻煩,該函數就是預先將口令保存,在讀證書時自動使用。
int SSL_CTX_use_PrivateKey_file(SSL_CTX* ctx,const char* file,int type);
加載自己的私鑰;type參數指明私鑰文件的結構類型;失敗返回-1。加載了證書和文件之後,就可以驗證私鑰和證書是否相符:
int SSL_CTX_check_private_key(SSL_CTX*);
示例:
if (SSL_CTX_use_certificate_file(ctx, server.crt, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(3);
}
SSL_CTX_set_default_passwd_cb_userdata(ctx, "123456789");
if (SSL_CTX_use_PrivateKey_file(ctx, server.key, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(4);
}
if (!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr,"Private key does not match the certificate public key/n");
exit(5);
}
2、關聯tcp套接字
SSL接連是依賴於socket套接字的,所以安全連接必須關聯相應套接字。SSL通信過程是在tcp連接建立之後進行的,所以套接字的建立過程跟普通的socket編程沒有什麼不同的,這裡不在贅述。
ssl = SSL_new (ctx); //申請SSL套接字
SSL_set_fd (ssl, sd); //綁定讀寫套接字
以上調用將連接成功的TCP套接字與SSL關聯。
3、開始安全通信
對於服務端調用
err = SSL_accept (ssl);
監聽客戶請求,對於客戶端調用
err = SSL_connect (ssl);
發起連接。成功之後,雙方就可以通過調用
int SSL_read(SSL* ssl, char* buf, int num);
int SSL_write(SSL* ssl, char* buf, int num);
進行安全通信了。
此時雙方可通過調用X509* SSL_get_peer_certificate(SSL* ssl)來獲取對方的證書信息
示例:
client_cert = SSL_get_peer_certificate (ssl);
if (client_cert != NULL) {
printf ("Client certificate:/n");
str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
CHK_NULL(str);
printf ("/t subject: %s/n", str);
OPENSSL_free (str);
str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);
CHK_NULL(str);
printf ("/t issuer: %s/n", str);
OPENSSL_free (str);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
X509_free (client_cert);
} else
printf ("Client does not have certificate./n");
4、通訊結束,需要釋放前面申請的SSL資源
int SSL_shutdown(SSL* ssl);關閉SSL套接字;
void SSL_free(ssl);釋放SSL套接字;
void SSL_CTX_free(ctx);釋放SSL環境;