openssl程序設計詳解

作者:Eric Rescorla on Sat, 2001-09-01 01:0
如果你急切的想構建一個簡單的Web客戶端和服務器對,這時你就需要使用SSL了..
SSL是一種保護基於TCP協議的網絡應用最快而且最簡單的的方法,如果你正在用C語言做開發,那麼對於你來說,最好的選擇可能就是使用OpenSSL了. OpenSSL是在Eric Young的SSLeay包的基礎上對TSL/SSL的一個免費的執行(類似於BSD方式的License).然而, 不幸運的事情是, 伴隨OpenSSL一起發佈的文檔和示例代碼並不是很完全, 使用它的人需要更多的東西.在OpenSSL被使用之處, man手冊都相當優秀,可是這些手冊失去了大的背景 因爲它們只是參考資料而不是教程.
OpenSSL的API多而複雜, 因此我們在此並不會作出一個完整的講述. 相反,我的目的只是教會你如何去高效的使用man手冊.在本文中, 我們將會通過構建一個簡單的Web客戶端和服務器來演示OpenSSL的基本特點. 而在後續的第二篇中我們將會介紹OpenSSL的一些高級特性, 比如會話恢復和客戶端認證等.
在話題開始之前, 我會認爲你已經熟悉SSL和HTTP了, 或者最起碼在概念層上應該有一些瞭解. 如果你對此一無所知, 推薦一個比較好的方法,那就是參考RFC(參見附錄).
由於篇幅原因, 本文只包涵了源代碼的一些摘錄, 完整的代碼可以從作者的站點http://www.rtfm.com/openssl-examples/上下載.
程序
我們的客戶端是一個簡單的HTTPS(見 RFC 281客戶端,它在初始化了一個到達服務器的SSL連接之後便通過這個連接將HTTP請求傳送給HTTP服務器. 然後等待服務器端的響應,並將響應信息打印在屏幕上.這是對通常那些”獲取並且打印信息”的程序功能更簡化的一個例子.
服務器端程序是一個簡單的HTTPS 服務器, 它等待從客戶端發出的TCP連接, 每當接收到一個連接時,它會磋商這個連接(的合法性). 一旦這個連接被確定下來, 它會讀取客戶端的HTTP請求, 並將HTTP請求的響應信息傳輸給客戶端. 當響應傳輸完畢時它會關閉這個連接.
我們的第一個任務就是建立一個上下文對象(一個SSL_CTX), 這個上下文對象會在每次需要建立新的SSL連接的時候被用來創建一個新的連接對象. 而這些連接對象則用於SSL的握手,讀和寫.
(使用上下文對象)這種方法有兩個優點: 首先, 上下文對象允許一次初始化多個結構體, 這樣就提提高了性能. 在大多數應用中, 每一個SSL連接都使用相同的加密算法(keying material)和CA(certificate authority)列表等. 而採用上面這種方法, 我們就不需要在每次連接的時都去加載這些信息(加密算法和證書), 而只需要在程序啓動時將它們加載進上下文對象中. 然後,當我們需要創建一個新的連接時, 只需要將新的連接簡單的指向這個上下文對象就可以了. 使用一個簡單的上下文對象的第二個好處就是它允許多個SSL連接之間共享數據, 比如用於SSL會話恢復的SSL緩衝(cache). 上下文初始化由主要的四個任務組成, 通過列表1所示的initialize_ctx()函數來完成.
列表 1 initialize_ctx()
在應用OpenSSL之前, 整個庫需要進行初始化, 這個過程通過SSL_library_init()函數來完成,它主要加載OpenSSL將會用到的算法, 如果我們想要很好的報告差錯信息, 同樣需要通過SSL_load_error_strings()來加載錯誤字符串, 否則, 就不能夠將OpenSSL的錯誤映射爲字符串.
我們同樣需要創建一個對象來作爲錯誤打印的上下文. OpenSSL爲輸入和輸出抽象了一個叫做BIO對象的概念.這樣可以使得程序員針對不同種類的IO通道(socket, 中斷,內存緩衝等)使用相同的函數,而唯一的差別就是在函數中使用的是不同種類的BIO對象.在本例中,我們通過將一個BIO對象與標準錯誤stderr綁定來打印錯誤信息.
如果你正在寫一個能夠執行客戶端認證的服務器或者客戶端程序, 你就需要加載自己的公鑰或者私鑰以及相關的證書.證書存儲空隙中, 並且通過SSL_CTX_use_certificate_chain_file()函數與CA證書一起被加載形成證書鏈表. SSL_CTX_use_PrivateKey_file()函數用來加載私鑰.出於安全原因, 私鑰通常通過密碼來加密, 如果使用密碼加密的話, 密碼回調函數(通過SSL_CTX_set_default_passwd_cb()來設置)將會在獲取密碼時被調用的.
如果你需要認證已經連接到你的客戶端, OpenSSL需要知道你信任哪些CA, SSL_CTX_load_verify_locations()調用用來加載CA.
爲了保證安全, OpenSSL需要一個好的強性隨機數源, 通常,爲隨機數生成器(RNG)提供種子原料是應用本身的責任, 然而,如果/dev/urandom可用的話,OPenSSL會自動的使用/var/urandom來爲RNG播種, 由於/dev/urandom在Linux是標準化的, 我們不需要爲它做任何事情, 這個就很方便了, 因爲收集隨機數很詭異,而且很容易引起系統抖動上升. 注意,如果你在一臺不是Linux的系統上,你可能會在某些時刻得到錯誤數據, 因爲隨機數產生器沒有被播種, OpenSSL的rand(3) man手冊爲你提供了更多可以參考的信息.
客戶端
當SSL完成了對SSL上下文對象的初始化後,它已經爲連接到服務器做好準備。OpenSSL要求我們自己建立一條從客戶端到服務器的TCP連接,然後使用這個TCP套接字創建一個SSL套接字.爲了方便期間,我們把TCP連接的創建劃分到函數tcp_connect()(這裏沒有給出實現,但是在下載的代碼中可以看的到)中去實現。
當TCP連接創建好以後,我們創建一個SSL對象來處理這個連接。這個對象需要與套接字綁定,注意,我們並不是直接把SSL對象綁定到套接字上,而是創建一個使用這個套接字的BIO對象, 然後將SSL對象綁定到這個BIO上。
這個抽象層允許你通過各種通道來使用OpenSSL而不是套接字,前提是你已經有了
一個合適的BIO對象。例如,有一個OpenSSL測試程序純粹通過內存緩衝區來連接SSL客戶端和服務器。一個比較實用的做法就是支持一些套接字根本無法訪問協議來進行連接。例如,你可以通過一個連續行(serial line)來運行SSL。
SSL連接的第一步就是執行SSL握手,握手鑑別服務器(也可以選擇鑑別客戶端客戶端)並且建立保護剩餘傳輸所需要的加密算法。SSL_connect() 調用用來執行SSL握手.由於我們使用
的是阻塞式的套接字,所以SSL_connect()函數在SSL握手沒有完成或者沒有檢測到一個差錯之前是不會返回的。成功時,這個函數返回1,返回0或者負數表示出錯。
調用如下:

CODE:
/* Connect the TCP socket*/
sock=tcp_connect(host,port);
/* Connect the SSL socket */
ssl=SSL_new(ctx);
sbio=BIO_new_socket(sock,BIO_NOCLOSE);
SSL_set_bio(ssl,sbio,sbio);
if(SSL_connect(ssl)<=0)
   berr_exit("SSL connect error");
if(require_server_auth)
   check_cert(ssl,host);

當我們初始化到達服務器的SSL連接時,我們需要先校驗服務器的證書鏈。OpenSSL爲我們做一些這樣的校驗,但是不幸的是,其他的校驗工作總是與具體的應用相關(所以不能通過OpenSSL來完成),因此,我們需要自己去做這些工作。我們的例子代碼做的主要測試就是檢驗服務器的身份。這個通過列表2的函數check_cert()來實現。
列表2 check_cert函數
當檢測到服務器的證書鏈有效的時候,你需要驗證你正在查看的證書與你期望的服務器所擁有的身份是否相匹配。在大多數情況下,這意味着服務器的DNS名字出現在證書中,或者在Subject Name 的Common Name域,或者位於證書的擴展部分(certificate extension).儘管每種協議在識別服務器身份的時候有少許的差別,但是RFC 2818包括了通過SSL/TSL識別HTTP服務器身份的規則。如果你沒有什麼很明瞭的意圖去做其他事情,按照RFC 2818 的規則去做是一個很好的做法.
由於大多數證書還一直將域名放在Common Name字段而不是擴展部分。所以我們只進行了
Common 字段的校驗,我們通過SSL_get_peer_certificate() 函數來提取服務器的證書,然後將證書的Common Name字段與我們連接的客戶機名稱進行比對,如果不匹配的話,肯定出錯了,我們退出程序。
在0.9.5版本之前,OpenSLL容易遭到一種證書擴展攻擊,爲了方便理解,我們考慮下面的情況,一個服務器鑑別由Bob簽名的證書,如圖1示,Bob並不是你的一個CA,但是它的證書卻是由你信任的一個CA簽名的。
如果你接受這個證書,你可能會有很大的麻煩,但是CA簽名了Bob的證書這個事實卻意味着它通過了對Bob的身份確認,但卻不是說Bob可以被信任.如果你知道你想要與Bob做生意,那很好,但是如果你想要與Alice(你從從來沒聽說過,但是Bob爲她擔保)和Bob一起做生意,那這些信息就沒有用了。
通常,保護你免於此類攻擊的唯一方法就是限制證書鏈的長度,目的就是使你明確你所觀看的證書就是CA簽名的。V3版的X.509證書包含了一種方法,它使得CA能夠在一些證書上做標籤說明這些證書是其他CA的。這種方法允許一個CA有一個簡單的根CA,然後根CA可以認證其他的輔助CA。
當前的OpenSSL(v0.9.5和以後的)都會校驗這些擴展,因此不論你是否校驗證書鏈的長度,這些擴展攻擊都會被自動防禦掉。比0.9.5更早的OpenSSL一點都不作這些擴展部分校驗,所以如果要使用這個版本之前的OpenSSL的話,你必須自己限制鏈的長度。0.9.5版的OpenSSL在校驗上有一些問題,所以如果你正在使用它你也許應該進行一些更新。initialize_ctx() 函數中,代碼#ifdefed提供了對老版本鏈長度的校驗,我們使用SSL_CTX_set_verify_depth()函數強迫OpenSSL去校驗鏈的長度。總之,強烈建議你升級到0.9.6,主要是因爲比較長的鏈(但是也有可能是故意構造的)越來越流行了,絕對最新和最好的版本就是OpenSSL 0.9.66了
我們使用列表3的代碼來寫HTTP請求,出於演示的目的,我們使用了或多或少的在REQUEST_TEMPLATE變量中可以找到的硬線路(HardWired)HTTP請求。由於連接的機器可能會改變,我們不需要填充Host這個頭信息。這個通過snprintf來實現。然後我們通過SSL_write()函數來發送數據到服務器端,SSL_write()的API或多或少與Write()函數類似,區別就是在write中我們傳遞文件描述符,而在前者中傳遞SSL對象。
列表3 HTTP請求。
有經驗的TCP程序員都知道,我們在函數的返回值不等於我們想要寫入的字節數時拋出一個錯誤,而不是循環的調用些函數. 在阻塞模式下,SSL_write()函數已經足夠,因爲在所有的數據都被寫完或者發生差錯之前,這個調用是不會返回的。然而write()函數卻可能只會寫入一部分數據,我們可以通過設置SSL_MODE_ENABLE_PARTIAL_WRITE標誌位來允許部分寫(本文沒有應用),在這種情況下,你需要循環的調用寫函數。
在老版本的HTTP1.0中,服務器傳輸它的HTTP響應然後關閉連接。在後來的版本中,引入的持續連接,支持同一連接上多個連續的事務。爲了方便和簡潔,在本文並不使用這種持續的連接。我們忽略(允許設置持續連接的)頭部信息,使服務器通過關閉連接來通知響應的結束。在操作上,意味着我們只需要持續讀,直到文件結束,這樣也相應的簡化了事務。
OpenSSL使用SSL_read() 函數來讀取數據,正如列表4中所示,跟使用read()一樣,我們只需要簡單的選擇一個合適大小的緩衝,然後將它傳遞給SSL_read()函數。注意到緩衝區的大小在此處並沒有多麼的重要,SSL_read() 和read()一樣,返回可用的數據,哪怕它比請求的數據量小. 另外,如果沒有數據可以讀取,讀函數將會阻塞。
列表4 讀取響應
BUFSIZZ的大小, 基本上可以說與性能是持平的, 這種性能持平與我們簡單的從普通的套接字讀取是不同的. 在那種情況下,對read的每一次調用都需要上下文切換到內核態去,由於上下文在內核態和用戶態之間切換是非常昂貴的, 程序員在讀取數據的時候都儘量使用較大的緩衝從而減少讀取的次數(從而減少了上下文切換的次數). 然而當我們在使用SSL的時候, 對read()的調用次數, 也就是上下文在內核態和用戶態切換的次數, 在很大程度上取決於數據寫入的記錄數而不是SSL_read()的調用次數.
例如,如果客戶端寫入了1000Byte的記錄, 然後我們調用SSL_read()每次讀取1Byte, 那麼對SSL_read()的第一次調用會使得所有的記錄被讀入, 然後剩下的調用就只是將記錄從SSL緩衝中讀出來.因此,在使用SSL而不是普通的套接字讀取數據時,緩衝區的大小選擇並不是特別的重要. 如果數據被寫成一系列小的記錄, 你可能想通過對Read的一次單獨的調用來讀取所有的記錄. 這時候, OpenSSL爲你提供了一個標誌位,那就是SSL_CTRL_SET_READ_AHEAD, 通過設置這個標誌位就可以打開這種讀的開關.
注意本文中使用switch語句來處理SSL_get_error()函數返回值這種用法,使用普通的套接字的一個方便之處就是任何的負的返回值(最典型的是-1)都代表失敗,然後你可以檢測errno去查看真正發生了什麼事情。但是errno在這裏並不起什麼作用,因爲它只代表了一個系統錯誤,而我們想要做的是對SSL錯誤進行處理。同樣編程時需要對errno進行細心的處理,以便實現線程安全。
在OpenSSL中提供了SSL_get_error()調用而不是errno, 這個調用使得我們可以檢測返回值以確定是否有錯誤發生,如果有錯誤發生,是什麼錯誤。如果返回值是一個正數,說明我們讀取到了一定的數據,這時候將它簡單的打印到屏幕上. 一個真正的客戶端會解析HTTP響應然後或者顯示數據或者將數據保存到磁盤。但是對OpenSSL而言,這些並沒有多大意義,所以我們在此處不會涉及對響應信息的具體處理。
如果返回值是0,並不表示沒有數據可以讀取,因爲在沒有數據可以讀取的情況下,正如上面已經討論過的一樣, 我們的函數肯定會被阻塞住的。所以,此處返回的0表明這個套接字已經被關閉了,當然也就沒有任何數據可以讀取了,所以我們退出循環。
如果返回值是一個負值,這時肯定發生了某種錯誤。我們只關心兩種類型的錯誤:普通錯誤和提前關閉的錯誤,我們使用SSL_get_error()函數來決定得到的是那種類型的錯誤,差錯處理在客戶端的程序中非常的簡單。所以對於大多數錯誤,我們僅僅使用berr_exit()函數來打印一行錯誤信息然後退出,然後,提前關閉這種錯誤需要進行特殊的處理.
TCP使用FIN片斷來表明發送者已經發送完所有的數據. SSL v2 允許任何一端通過發送TCP FIN字段來結束SSL連接. 但是,這種原則卻會遭受一種截斷攻擊,攻擊者可以自己僞造一個TCP FIN來終止連接,使得發送的數據比實際要發送的少. 除非受害者有某種方法知道他將要接收多長的數據,否則她/他很容易會認爲接收到的那一部分長度的數據已經是所有的了。
爲了解決此種安全隱患,SSL v3引入了close_notify()警報機制,close_notify是一個SSL消息(因此是安全的)但卻不是SSL數據流的一部分,因此應用程序並不能看到它。在close_notify消息被髮送出去之後,任何數據將不能再傳輸了。
因此當SSL_read()返回0表示套接字已經被結束時,這其實意味這close_notify消息已經收到,如果客戶端在收到close_notify消息之前收到一個FIN,SSL_read()將會返回一個錯誤,這種情況叫做提前關閉。
一個比較簡單的客戶端可能在遇到任何一個提前關閉的情況時都會報告一個錯誤然後退出.
這個處理是SSL V3採取的默認處理方式,但是,不幸的是,對於客戶端來說,發送提前關閉消息是一個很常見的差錯。所以,如果你不是爲了一直彙報錯誤的話,你最好忽略這些提前關閉消息。我們的代碼進行了特別的處理,它報告錯誤卻不隨着錯誤而退出程序。

如果在讀取響應的時候沒有發生任何錯誤,這時我們就需要發送自己的close_notify 消息給服務器端,這個是通過SSL_shutdown() 函數來實現的,在討論服務器端的時候我們會仔細的研究這個函數的。但是大體的思想卻很簡單:返回1表示完全關閉,0表示不完全關閉,-1表示出錯。由於我們已經收到了服務器端發送的close_notify消息,所以唯一可能出現的問題就是我們在發送我們自己的close_notify消息時出了差錯,要不然的話,SSL_shutdown()函數將會成功的(返回值爲1)
最後,我們需要銷燬申請的變量對象,因爲這個程序最終要退出的,釋放這個操作並不是嚴格意義上必須的進行的,但是在更一般的程序中它卻是必要的。
服務器
我們的Web服務器除了比客戶端更復雜點外, 可以說是客戶端的一個鏡像,首先,爲了服務器能處理多個客戶端,要要調用fork()來創建子進程,然後我們用OpenSSL使用的BIO對象的API來一次一行的讀取客戶端的請求,同樣用BIO來實現對客戶端的緩衝寫,最後,服務器端的關閉過程有些複雜。
通常在一臺Linux系統上,服務器處理多個客戶端連接最簡單的方法就是爲每個客戶端fork()出一個子進程,我們在這裏是在accept()返回之後通過fork()來創建子進程。每一個子進程獨立運行並且在對客戶端進行服務以後自行退出。儘管這種方法在比較繁忙的Web服務器上可能相當慢,但是在此處卻是可以接受的,列表5是主服務器的accept循環
列表5 服務器接收連接循環
在fork()和創建SSL對象之後,服務器調用SSL_accept()函數,從而引起OpenSSL執行了服務器端的SSL握手,跟使用SSL_connect()一樣,由於我麼使用的是阻塞式的套接字,所以SSL_accept()函數將會一直阻塞直到握手完成爲止。因此,SSL_accept()返回的唯一情況就是握手完成或者檢測到錯誤。SSL_accept()返回1表示成功,返回0或者負數表示失敗。OpenSSL的BIO對象在某種程度上有棧的特性,因此我們可以把SSL對象封裝在BIO(SSL_BIO對象)中,然後把那個BIO封裝在一個緩衝的BIO對象中,如下所示:
CODE:
                              io=BIO_new(BIO_f_buffer());
       ssl_bio=BIO_new(BIO_f_ssl());
    BIO_set_ssl(ssl_bio,ssl,BIO_CLOSE);
       BIO_push(io,ssl_bio);

這種方法允許我們使用BIO_* 函數族來操作新類型的IO對象,從而實現對SSL連接的緩衝讀和寫。在此處,你也許會問,爲什麼這個用法更好?(或者這有什麼好的)。主要的原因是,這種方法編程起來很舒服,它使得程序員能夠去處理一種更自然的單元(行或者字符等)而不是SSL記錄。
請求
HTTP請求由請求信息行後面跟着一堆頭信息行再加上一個可選體組成。頭信息行是通過空行來結束的(例如,一對CRLF,有時候崩潰的客戶端會發送一對LF),最舒服的讀取請求信息行和頭信息行的方式就是一次讀取一行,直到讀取到空行爲止。我們可以使用列表6中的OpenSSL_BIO_gets()調用來實現這個操作。
列表6 讀取請求
OpenSSL_BIO_gets()調用表現的類似於標準輸入輸出調用fgets(),它使用任意大小的緩衝區和長度從SSL連接中讀取一行數據到緩衝中去,讀取的結果通常以空字符結束(但也包括結束符LF)。因此,我們每次簡單的讀取一行,直到讀到某一行包括一個簡單的LF或者CRLF。
由於我們使用固定大小的緩衝,所以有可能,也許不太可能,我們會讀取到很長的一行,在這種
情況下,這一行將被分解成兩行,在極端不可能的情況下,分隔正好在CRLF之前發生,這樣的話,從前一行讀取到的第二行就只包括一個CRLF了,這時候我們就會迷惑,認爲頭序列提前結束了。一個真正的Web服務器會檢測這種情況的,但是在這裏卻不值得去做。注意,不管到達的行數是多少,都不會有緩衝區溢出的情況發生。所有可能發生的就是我們會錯誤的解析頭信息。
注意到我們並不需要用HTTP請求做任何事情,所以只是讀取然後將它丟棄。真正的實現將會讀取請求信息行和頭信息行,計算是否有一個消息體存在然後讀取這個消息體。
下一步就是寫HTTP響應並且關閉連接:
CODE:
if((r=BIO_puts
    (io,"HTTP/1.0 200 OK//r//n"))<0)
    err_exit("Write error");
if((r=BIO_puts
    (io,"Server: EKRServer//r//n//r//n"))<0)
    err_exit("Write error");
if((r=BIO_puts
    (io,"Server test page//r//n"))<0)
    err_exit("Write error");
if((r=BIO_flush(io))<0)
    err_exit("Error flushing BIO");

注意我們在程序中使用BIO_puts()而不是SSL_write()。這樣我們就可以一次寫一行響應消息,而把所有的行當作一條SSL記錄發送出去,這種做法是很重要的,因爲準備(計算完整性,校驗,加密等)一個SSL傳輸記錄的花銷是非常大的。因此,使一條傳輸的記錄儘可能的大是一個很好的主意。
我們有必要留意一下所使用的緩衝寫方法. 首先,在關閉之前你需要衝掉緩衝區,SSL對象並不知道你已經在它上面佈置了一層BIO,所以,如果你破壞了SSL連接,將會使得剩餘截斷的數據留在緩衝區中。BIO_flush() 函數是用來處理這個的。同樣,默認情況下,OpenSSL爲BIO對象使用了1024Byte大小的緩衝區,由於SSL記錄大小可以長達16K,所以使用1024Byte大的緩衝可能會引起過多的碎片(從而使效率下降),你可以使用BIO_ctrl() 函數來增加緩衝區的大小。
一旦完成了響應的傳送,我們需要發送close_notify消息,前面已經講到了,是通過SSL_shutdown來實現的,不幸的是,當服務器首先關閉的時候,情況變得遊戲蹊蹺。我們對SSL_shutdown() 的第一次調用會發送close_notify消息,但是在另一端卻不會取尋找它。所以它會很快的以0作爲結果返回,表明關閉過程沒有完成。然後,就需要應用程序自身再一次調用SSL_shutdown()函數了。
這裏也可以存在兩種觀點,我們能夠肯定已經看到了自己關注的HTTP請求的整個部分,然後對其他都不感興趣,因此,我們可以並不在乎客戶端是否發送了close_notify消息,相反,如果嚴格的遵守協議並且要求其它人也這麼作的話,我們也就就需要收到一個close_notify消息。
如果堅持第一種觀點的話,一切將會變得很簡單,我們簡單的調用SSL_shutdown() 函數發送我們的close_notify消息,然後不管客戶端是否發送一個close_notify 消息,就立刻退出。如果堅持第二種觀點的話(本文的例子服務器就是這麼做的),事情就變得比較複雜了,因爲客戶端通常都不會表現的多麼正常。
我們面臨的第一個問題就是客戶端通常都不會發送close_notifys消息。事實上,有些客戶端在它們收到HTTP響應時便會立即關閉連接(有些IE是這麼做的),當我們在發送close_notify時,另一端可能正在發送一個TCP RST字段,在這種情況下,程序將會捕獲SIGPIPE信號,在本文, 我們將會在函數initialize_ctx()中安裝一個虛設的SIGPIPE信號處理器來避免這種情況的發生。
我們面臨的另外一個問題就是客戶端可能不會立即發送一個close_notify消息來作爲對服務器端close_notify消息的迴應,一些版本的Netscape 要求你首先發送一個TCP FIN標誌。因此我們在第二次調用SSL_shutdown()之前調用了shutdown(s,1)函數,當我們使用1作爲第一個參數時,shutdown()函數發送了
一個FIN標誌,但是卻使得套接字處於打開並且讀的狀態。服務器端關閉的代碼如列表7所示.
列表7 訪問SSL_shutdown()
其它相關的東西
在本文,我們只是提及了使用OpenSSL時的一些表面的觀點,下面是更多的一些觀點:
一種更復雜的檢測證書中服務器名字的方法就是使用X.509當中的subjectAltName擴展部分。爲了做這個檢測,我們需要從證書中提取出這個部分來,然後根據hostname檢測這個部分,同樣,能夠在證書中根據wild-carded 名字來檢測主機名也是非常有意思的事情。
注意這些程序處理差錯都是根據錯誤簡單的退出程序,一個真正的應用將會識別出錯誤類型然後發信號告訴給用戶或者一些審計日誌,而不是直接退出。
下篇文章中,我們將會討論一些OpenSSL的高級特性,包括會話恢復,多路傳輸,非阻塞IO以及客戶端認證等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章