問題描述
我們某安卓盒子的客戶端,每次開機第一次連接服務器大概率出現ssl錯誤導致連接失敗,openssl返回的錯誤是:
SSL_connect error:00000001:lib(0):func(0):reason(1)
抓取網絡pcap包,發現在ssl握手時,客戶端收到服務器的證書認爲證書無效,但是同樣的證書過一會再連接,又可以建立連接。此問題僅發生在某特定型號的盒子
問題分析
分析了一下證書的認證過程,無外乎判斷一下證書有效時間,雙方支持的加密格式等等,最終發現是安卓端的時間有問題:系統是不保存時間的,每次開機從網絡校時,經常出現校時延誤甚至校時失敗,拿一個2014年的初始時間去檢查證書的時間,所以認定證書“過期”了。
這裏一個坑的地方是,盒子上面顯示的是桌面app獲取的時間,不是底層系統的時間,底層系統的時間可以在shell裏通過date命令查看。
另外一個坑,openssl的錯誤信息可能一次get不完,需要get多次才能把所有信息打印出來,修正了一下打印錯誤信息的代碼
char error_str[256];
while (rc != 0) {
ERR_error_string_n(rc, error_str, sizeof(error_str));
g_warning("%s: SSL_connect %s", c->name, error_str);
rc = ERR_get_error();
}
所以問題完整錯誤信息應該是:
SSL_connect error:00000001:lib(0):func(0):reason(1)
SSL_connect error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
問題解決
作爲一個第三方應用開發人員,對android rom的bug無可奈何,普通應用連設置系統時間的權限都都沒有。當然可以通過設置證書的有效期在2014年開始規避,這顯然不是好辦法。
比較合理的辦法是通過我們app先獲取一個正確時間,再拿這個時間去校準,而不是讓openssl使用系統返回的時間。
修改openssl的庫,讓openssl提供一個可以設置校準時間的接口:
int SSL_connect_ptime(SSL *s, time_t ptime)
{
if (ptime)
X509_VERIFY_PARAM_set_time(s->param, ptime);
return SSL_connect(s);
}
用過ssl的同學應該熟悉這個SSL_connect()
,增加一個openssl的接口必須在util/libssl.num
文件上加上你的函數名,注意後面的序號和版本號
SSL_COMP_get_id 412 1_1_0d EXIST::FUNCTION:
SSL_COMP_get0_name 413 1_1_0d EXIST::FUNCTION:
+SSL_connect_ptime 414 1_1_0d EXIST::FUNCTION:
FIXED & END