我們目前大部分使用的openssl庫還是基于TLS1.2協(xié)議的1.0.2版本系列,如果要支持更高的TLS1.3協(xié)議 , 就必須使用openssl的1.1.1版本或3.0版本 。升級openssl庫有可能會導(dǎo)致SSL會話失敗,我在升級 wincurl 時,意外的收獲了一個函數(shù) 。這個函數(shù)非常的不起眼,但具有的現(xiàn)實(shí)意義卻很大 。
大部分情況下如果你不調(diào)用該函數(shù),并不影響SSL會話和通信,但有時會被某些服務(wù)器拒絕 。一旦被拒絕,查找具體的原因?qū)⒆兊梅浅M纯?。這個函數(shù)的意義好比HTTP協(xié)議中HOST字段,它和NGINX反向代理的Server name有異曲同工之妙 。了解這個函數(shù)的意義 , 可能會讓你今后在配置nginx反向代理時少走一些彎路 。
這個函數(shù)是 SSL_set_tlsext_host_name,在介紹這個函數(shù)之前,我們先快速看看TLS協(xié)議和openssl的發(fā)展 。
雖然TLS1.3的標(biāo)準(zhǔn)自2018年就已發(fā)布,但目前國內(nèi)幾乎所有網(wǎng)站并沒有增加對TLS 1.3的支持 , 大部分主流網(wǎng)站使用的還是基于TLS1.2的版本 。目前國內(nèi)使用TLS1.3的好像只有知乎,其它諸如:新浪、網(wǎng)易、搜狐、騰訊、華為、京東、百度....都還是采用TLS1.2的標(biāo)準(zhǔn) 。
網(wǎng)站是否支持TLS1.3其實(shí)并不重要,重要的是瀏覽器是否支持TLS1.3協(xié)議 。好在目前市場上所有主流瀏覽器都已升級到了TLS1.3 。因此對于用戶而言,不管是支持TLS1.3的網(wǎng)站還是支持TLS1.2的網(wǎng)站 , 訪問起來都不是問題,或者說對終端用戶而言這種訪問是無感的 。
為何TLS3.0已經(jīng)出現(xiàn)5年了,各大網(wǎng)絡(luò)平臺還是首選TLS1.2呢?估計還是出于對兼容和穩(wěn)定的考慮 。如果貿(mào)然升級到1.3導(dǎo)致大量客戶端無法訪問從而丟失用戶,是任何平臺所無法承受的 。此外,支持TLS1.3的openssl版本也在不斷完善中 , 在其未達(dá)到穩(wěn)定前,最好還是使用穩(wěn)定的1.2版本 。這大概就是目前幾乎所有平臺類網(wǎng)站還在使用TLS1.2的原因 。
此外,即使有的網(wǎng)站采用TLS1.3協(xié)議 , 也會繼續(xù)提供對1.2的訪問支持(向下兼容并保留所有TLS1.2的加密套件),除非該網(wǎng)站的運(yùn)維人員強(qiáng)行使用TLS1.3的加密套件 。但如果這樣做的話就會導(dǎo)致大量仍然使用1.2標(biāo)準(zhǔn)的客戶端程序無法繼續(xù)訪問該網(wǎng)站 , 這也意味著該運(yùn)維人員離下崗再就業(yè)不遠(yuǎn)了 。
這也是為什么我們即使不升級老版本的openssl庫,也依然可以訪問TLS1.3網(wǎng)站的原因 。
上面說的TLS1.2,1.3指的是TLS的協(xié)議,實(shí)現(xiàn)上述協(xié)議的是openssl庫 。其中支持TLS1.3的openssl庫目前有兩個版本系列:一個是1.1.1系列,一個是3.0系列 。其中1.1.1更像是一個過渡版本,openssl團(tuán)隊承諾會支撐該版本到2023年9月11日,也就是明年911事件22周年之際(確實(shí)是一個值得紀(jì)念的日子)將停止更新1.1.1版本 。這就意味著大家還沒開始普及使用1.1.1 , 它就已經(jīng)結(jié)束了 。
這個其實(shí)也不重要,因為3.0才是最終openssl的終極版本 , 直接跳過了2.0,從而體現(xiàn)了openssl的跨越式發(fā)展 。3.0版本是openssl團(tuán)隊主推版本,也是一個長期維護(hù)的版本,該版本會維護(hù)到2026年9月7日 。
雖然主流web瀏覽器都已支持TLS1.3,但瀏覽器只是客戶端的一種,其它大部分客戶端使用的還是openssl 1.0.2系列版本,甚至是1.0.1系列版本,也就是我們經(jīng)常看到的libeay32.dll和ssleay32.dll 庫,這些版本是不支持TLS1.3協(xié)議的 。比如工具類的postman、curl,以及應(yīng)用類QQ、Foxmail、微信以及各種下載客戶端程序,使用的可能還是老的openssl庫 。openssl庫在1.1.1及以上版本已經(jīng)將庫的導(dǎo)出名稱改為libcrypto和libssl,不過這只是前綴名,如果是1.1.1系列,則后綴為-1.1.dll,如果是3.0系列 , 則后綴為-3.dll,我們通過openssl的庫名就能判斷出該應(yīng)用程序是否支持TLS1.3 。
升級到TLS1.3可以從openssl的官網(wǎng)(https://openssl.org)下載1.1.1或3.0版本的源碼進(jìn)行編譯 。編譯完畢后,我們只要替換openssl庫,并重新配置并編譯你的客戶端程序即可 。
使用openssl庫編寫客戶端程序的一般流程如下:先創(chuàng)建套接字 , 并連接到服務(wù)器,然后創(chuàng)建ssl,并綁定套接字 , 通過調(diào)動SSL_connect函數(shù)進(jìn)行握手操作,成功后使用SSL_read和SSL_write進(jìn)行業(yè)務(wù)通信 。
代碼通常如下:
【openssl升級后ssh登錄失敗】 // 創(chuàng)建套接字,并連接到服務(wù)器 SOCKET s = socket(AF_INET, SOCK_STREAM, 0); struct hostent* hst = ::gethostbyname(pszServer); if(NULL == hst) return FALSE; unsigned long addr; struct sockaddr_in sockAddr; memcpy(&addr, hst->h_addr, hst->h_length); sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons(nPort); sockAddr.sin_addr.S_un.S_addr = addr; // 連接到服務(wù)器 if(SOCKET_ERROR != ::connect(s, (struct sockaddr *)&sockAddr, sizeof(struct sockaddr_in))) return FALSE; // Openssl庫初始化 OpenSSL_add_ssl_algorithms(); SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); ERR_load_BIO_strings(); ctx = SSL_CTX_new(SSLv23_client_method()); // 創(chuàng)建ssl上下文,并綁定套接字 SSL* ssl = SSL_new (m_ctx); SSL_set_fd(m_ssl, s); // 開始ssl握手 int iRet = SSL_connect(m_ssl); if(1 != iRet) return FALSE; // 下面使用SSL_read或SSL_write進(jìn)行通信 上述代碼中SSL_connect函數(shù)是最重要的,它內(nèi)部實(shí)現(xiàn)了SSL的握手過程,openssl在內(nèi)部采用狀態(tài)機(jī)的方式實(shí)現(xiàn)了整個握手,代碼還是相當(dāng)?shù)幕逎?。幾乎所有的SSL通信失敗都是在握手階段產(chǎn)生的 , 比如加密套件不匹配,服務(wù)器證書沒有可靠的簽名等 。
openssl中有個s_client命令集合,該命令用于實(shí)現(xiàn)客戶端的通信,它的實(shí)現(xiàn)在s_client.c文件中,在這個龐大代碼中有一個不起眼的函數(shù)調(diào)用 , 這個函數(shù)是SSL_set_tlsext_host_name 。該函數(shù)的作用是在客戶端在發(fā)送ClientHello消息時,將所訪問的主機(jī)(服務(wù)器)名稱寫入到server name的擴(kuò)展字段中 。
if (!noservername && (servername != NULL || dane_tlsa_domain == NULL)) { if (servername == NULL) { if(host == NULL || is_dNS_name(host)) servername = (host == NULL) ? "localhost" : host; } if (servername != NULL && !SSL_set_tlsext_host_name(con, servername)) { BIO_printf(bio_err, "Unable to set TLS servername extension.\n"); ERR_print_errors(bio_err); goto end; } }調(diào)用該函數(shù)后會在ClientHello的擴(kuò)展字段中增加一個Server Name字段 。如下圖所示:

文章插圖
我們在客戶端編程時,通常不會調(diào)用該函數(shù) , 因為會覺得該字段可有可無 , 既然已經(jīng)連接到Host服務(wù)器上,為何還要將Host的名稱告訴給服務(wù)器呢?如果我們不調(diào)用SSL_set_tlsext_host_name 函數(shù) , 難道SSL會話會失敗么?實(shí)際上即使不調(diào)用該函數(shù),也是可以握手成功的,并不影響TLS的正常通信 。
但如果服務(wù)器強(qiáng)制校驗該字段時 , 就會導(dǎo)致握手失敗,這就好比HTTP協(xié)議中請求的HOST字段一樣 。那么為什么有的服務(wù)器要強(qiáng)制校驗該字段呢?如果一臺服務(wù)器綁定不同的DSN名稱(也就是一個IP地址綁定多個域名),這個擴(kuò)展字段的意義就出來了,服務(wù)器會根據(jù)不同的域名提供不同的證書 。
舉個例子,假設(shè)BAT都破產(chǎn)了,他們窮到共用一臺服務(wù)器的地步,也就是一個IP地址綁定了百度、阿里和騰訊三個公司域名,并且這三家公司都給域名申請了數(shù)字證書,此時擴(kuò)展字段的HOST的作用就體現(xiàn)出來了,openssl服務(wù)器端會跟據(jù)不同的host名稱決定返回哪家公司的數(shù)字證書 。這也是Ngnix反向代理的原理,根據(jù)不同域名進(jìn)行不同訪問地址映射 。
寫到這里,本文也即將結(jié)束 , 也許我們調(diào)用了SSL_set_tlsext_host_name函數(shù)也無法知道它的最終目的是什么,這也是我寫這篇文章的原因之一吧 。
來源:https://www.cnblogs.com/softlee/p/16416574.html
- 父母有權(quán)在離婚后拒絕撫養(yǎng)費(fèi)
- ai輪廓如何才可以取消,ai不小心點(diǎn)到輪廓以后怎么還原
- 成年德牧換主有危險,泰迪狗換主人后的表現(xiàn)
- 系統(tǒng)u盤多大合適,u盤裝系統(tǒng)后如何把u盤恢復(fù)到正常
- 羊毛水洗后怎么補(bǔ)救,純羊毛西褲水洗后怎么辦有色差了起毛了
- 離婚可以斷絕子女關(guān)系嗎,離婚后與孩子徹底斷絕關(guān)系
- 車輛被撞后貶值損失誰來賠償
- 哪款手機(jī)可以升級鴻蒙系統(tǒng)3.0 鴻蒙系統(tǒng)3.0哪些手機(jī)可以升級
- 容留他人吸毒罪什么后果
- 相機(jī)權(quán)限被禁用怎么辦,oppo手機(jī)軟件停用后怎么開啟
