在產(chǎn)品使用中,實(shí)施人員常常報(bào)告服務(wù)器與客戶端無法連接.究其原因是因?yàn)榭蛻舳藱C(jī)器與服務(wù)端機(jī)器系統(tǒng)時間不一致.原因在于系統(tǒng)使用了OpenSSL,證書中有一個有效時間段,當(dāng)客戶端或服務(wù)器的系統(tǒng)時間不在這個時間段內(nèi)時SSL會因證書驗(yàn)證失敗而無法連接.在實(shí)施中系統(tǒng)時間錯誤是很常見的,因不能上網(wǎng)而未開時間自動同步,bios沒電了,客戶疏忽等原因都會導(dǎo)致系統(tǒng)時間設(shè)置有誤.如果連接失敗后再查看系統(tǒng)時間設(shè)置總是一項(xiàng)麻煩的事情,那么有哪些辦法可以自動避免這個問題呢?
一,將證書的有效期設(shè)得夠大:如:1970-2099
這樣估計(jì)可以在一定程度上解決這個問題,不過這也是個餿主意.
二,檢測及必要時自動同步客戶端與服務(wù)器的時間
通過用wireshake抓包分析SSL建立連接的過程,發(fā)現(xiàn)在SSL握手過程中,會向?qū)Ψ絺魉捅緳C(jī)的系統(tǒng)時間.因此一個顯而易見的辦法就是獲取對方的時間,然后在必要時將本機(jī)的系統(tǒng)時間改為對方的系統(tǒng)時間,失敗后再連一次.下面是具體的示例代碼:
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
typedef struct _TimeInfo
{
time_t client; /*客戶端的時間*/
time_t server; /*服務(wù)器的時間*/
} TimeInfo;
/**
* 同步系統(tǒng)時間.
*/
BOOL syncSystemTime(time_t t)
{
SYSTEMTIME st;
FILETIME ft;
LONGLONG ll;
ll = Int32x32To64(t, 10000000) + 116444736000000000; //1970.01.01
ft.dwLowDateTime = (DWORD)ll;
ft.dwHighDateTime = (DWORD)(ll >> 32);
return FileTimeToSystemTime(&ft, &st) && SetSystemTime(&st);
}
/**
* 獲取SSL握手過程中服務(wù)器與客戶端雙方的系統(tǒng)時間.
*/
void getSSLHandleShakeTimeInfo(int write_p,
int version,
int content_type,
const unsigned char* buf,
size_t len,
SSL *ssl,
TimeInfo *ti)
{
if(content_type != 22) //require handshake message
return;
if(len < 42)
return;
if(buf[0] == 1) //ClientHello Message send from client to server
ti->client = htonl(*((u_long*)(buf + 6)));
else if(buf[0] == 2) //ServerHello Message send from server to client
ti->server = htonl(*((u_long*)(buf + 6)));
else
return;
}
int main()
{
BIO * bio;
SSL * ssl;
SSL_CTX * ctx;
TimeInfo timeInfo = {-1, -1};
BOOL timeSynced = FALSE;
long result;
/* Set up the library */
SSL_library_init();
ERR_load_BIO_strings();
SSL_load_error_strings();
/* Set up the SSL context */
ctx = SSL_CTX_new(SSLv3_client_method());
if(ctx == NULL)
{
fprintf(stderr, "Error new SSL_CTX\n");
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return 0;
}
/* Get Server and Client system time via SSL Handshake */
SSL_CTX_set_msg_callback(ctx, getSSLHandleShakeTimeInfo);
SSL_CTX_set_msg_callback_arg(ctx, &timeInfo);
/* Load the trust store */
if(! SSL_CTX_load_verify_locations(ctx, ".\\certs\\cacert.pem", NULL))
{
fprintf(stderr, "Error loading trust store\n");
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return 0;
}
/* Setup the connection */
bio = BIO_new_ssl_connect(ctx);
/* Set the SSL_MODE_AUTO_RETRY flag */
BIO_get_ssl(bio, & ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
/* Create and setup the connection */
BIO_set_conn_hostname(bio, "192.168.1.5:5555");
if(BIO_do_connect(bio) <= 0)
{
fprintf(stderr, "Error attempting to connect\n");
ERR_print_errors_fp(stderr);
BIO_free_all(bio);
SSL_CTX_free(ctx);
return 0;
}
/* Check the certificate */
switch(SSL_get_verify_result(ssl))
{
case X509_V_OK:
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_CERT_HAS_EXPIRED:
if(timeInfo.server != -1 && timeInfo.client != -1)
{
printf("當(dāng)前客戶端時間: %s", ctime(&timeInfo.client));
printf("當(dāng)前服務(wù)器時間: %s", ctime(&timeInfo.server));
printf("嘗試與服務(wù)器時間同步
");
if(syncSystemTime(timeInfo.server))
printf("成功\n");
else
printf("失敗\n");
printf("請重試連接服務(wù)器!\n");
}
default:
fprintf(stderr, "Certificate verification error: %i\n", SSL_get_verify_result(ssl));
BIO_free_all(bio);
SSL_CTX_free(ctx);
return 0;
}
/* Close the connection and free the context */
BIO_free_all(bio);
SSL_CTX_free(ctx);
return 0;
}