[英]C++ OpenSSL Fails to perform handshake when accepting in non-blocking mode. What is the proper way?
我正在嘗試在使用原始C套接字的應用程序中實現OpenSSL,而我唯一遇到的問題是代碼的SSL_accept / SSL_connect部分,該部分啟動KeyExchange階段,但似乎並未在服務器端完成它。
我已經在StackOverflow上瀏覽了無數的網站和問答,以了解OpenSSL API,因為這基本上是我第一次嘗試在應用程序中實現SSL,但唯一找不到的就是如何正確處理失敗的握手。
基本上,作為服務器運行的進程A將偵聽傳入的連接。 一旦我運行充當客戶端的進程B,它將成功連接到進程A,但SSL_accept(在服務器上)失敗,錯誤代碼為-2 SSL_ERROR_WANT_READ。
根據openssl握手失敗 ,可以通過在循環內調用SSL_accept直到最終返回1(成功連接並完成握手)來“輕松”解決問題。 但是,我不認為這是正確的處理方式,因為它看起來像是骯臟的把戲。 之所以認為這是一個骯臟的把戲,是因為我試圖運行一個在https://www.cs.utah.edu/~swalton/listings/articles/(ssl_client和ssl_server)上找到的小應用程序,一切正常。 沒有多次調用SSL_accept,握手立即完成。
這是我在服務器上接受SSL連接的一些代碼:
if (SSL_accept(conn.ssl) == -1)
{
fprintf(stderr, "Connection failed.\n");
fprintf(stderr, "SSL State: %s [%d]\n", SSL_state_string_long(conn.ssl), SSL_state(conn.ssl));
ERR_print_errors_fp(stderr);
PrintSSLError(conn.ssl, -1, "SSL_accept");
return -1;
}
else
{
fprintf(stderr, "Connection accepted.\n");
fprintf(stderr, "Server -> Client handshake completed");
}
這是PrintSSLError的輸出:
SSL State: SSLv3 read client hello B [8465]
[DEBUG] SSL_accept : Failed with return -1
[DEBUG] SSL_get_error() returned : 2
[DEBUG] Error string : error:00000002:lib(0):func(0):system lib
[DEBUG] ERR_get_error() returned : 0
[DEBUG] errno returned : Resource temporarily unavailable
這是連接到服務器的客戶端代碼段:
if (SSL_connect(conn.ssl) == -1)
{
fprintf(stderr, "Connection failed.\n");
ERR_print_errors_fp(stderr);
PrintSSLError(conn.ssl, -1, "SSL_connect");
return -1;
}
else
{
fprintf(stderr, "Connection established.\n");
fprintf(stderr, "Client -> Server handshake completed");
PrintSSLInfo(conn.ssl);
}
已成功在客戶端建立連接(SSL_connect不返回-1),並且PrintSSLInfo輸出:
Connection established.
Cipher: DHE-RSA-AES256-GCM-SHA384
SSL State: SSL negotiation finished successfully [3]
這就是我將C套接字包裝到SSL中的方法:
SSLConnection conn;
conn.fd = fd;
conn.ctx = sslContext;
conn.ssl = SSL_new(conn.ctx);
SSL_set_fd(conn.ssl, conn.fd);
這里的代碼段位於一個函數中,該函數在原始套接字上使用接受的傳入連接的文件描述符以及要使用的SSL上下文。
要初始化SSL上下文,我使用TLSv1_2_server_method()和TLSv1_2_client_method()。 是的,我知道如果客戶端不支持TLS 1.2,這將阻止客戶端連接,但這正是我想要的。 無論誰連接到我的應用程序,都必須通過我的客戶端來完成。
無論哪種方式,我在做什么錯? 我想避免在身份驗證階段出現循環,以免由於意外的無限循環而導致應用程序掛斷/放慢速度,因為OpenSSL未指定可能進行的嘗試次數。
有效但我要避免的變通方法是:
while ((accept = SSL_accept(conn.ssl)) != 1)
在while循環中,我檢查存儲在accept中的返回碼。
我嘗試解決SSL_ERROR_WANT_READ錯誤的方法:
我也使用openssl命令行進行了一些測試,以解決問題,但沒有給出錯誤。 握手似乎成功,因為沒有錯誤,例如:
24069864:error:1409E0E5:SSL routines:ssl3_write_bytes:ssl handshake failure:s3_pkt.c:656
出現。 這是命令的全部輸出
openssl s_client -connect IP:Port -tls1_2 -prexit -msg
注意事項:1.我正在使用最新的OpenSSL版本1.0.2h 2.應用程序在Unix系統上運行3.使用自簽名證書來加密網絡流量
謝謝所有將幫助我的人。
編輯:我忘了提到套接字處於非阻塞模式,因為該應用程序一次為多個客戶端提供服務。 但是,在客戶端,它們處於阻止模式。
Edit2:將此留作以后參考:jmarshall.com/stuff/handling-nbio-errors-in-openssl.html
您已經說明套接字問題是非阻塞的。
好吧,這就是你的答案。 顯然,當套接字處於非阻塞模式時,握手無法立即完成。 握手過程涉及客戶端與服務器之間交換協議數據包,每個數據包都必須等待接收其對等方的響應。 當套接字處於默認阻止模式時,這可以很好地工作。 該庫只不過是read()
和write()s
,它們阻塞並等待直到成功讀取或寫入消息為止。 當套接字處於非阻塞模式時,這顯然不會發生。 如果沒有要讀取的內容或套接字的輸出緩沖區已滿,則read()
或write()
立即成功或失敗。
SSL_accept()
和SSL-connect()
的手冊頁介紹了當基礎套接字處於非阻塞模式時執行SSL握手必須執行的過程。 您應該自己閱讀手冊頁,而不是在這里重復整個過程。 膠囊摘要將使用SSL_get_error()
確定握手是否確實失敗,或者庫是否要從套接字讀取或寫入套接字; 並最終調用poll()
或select()
,然后再次調用SSL_accept()
和SSL_connect()
。
任何其他方法,如在各處隨地撒些傻的sleep()
調用,都會導致紙牌屋不可靠,從而隨機失敗。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.