在項目中我們需要調用https接口請求。我們使用httpClient,構建HttpClient對象時涉及到使用HostnameVerifier接口實例。
HostnameVerifier接口定義如下:
1 public abstract interface HostnameVerifier 2 { 3 public abstract boolean verify(String paramString, SSLSession paramSSLSession); 4 }
僅一個方法,參數paramString為請求地址host;參數paramSSLSession是當前請求的SSLSession,可以獲取到證書列表,默認實現類DefaultHostnameVerifier的實現如下:
1 @Override 2 public boolean verify(final String host, final SSLSession session) { 3 try { 4 final Certificate[] certs = session.getPeerCertificates(); 5 final X509Certificate x509 = (X509Certificate) certs[0]; 6 verify(host, x509); 7 return true; 8 } catch (final SSLException ex) { 9 if (log.isDebugEnabled()) { 10 log.debug(ex.getMessage(), ex); 11 } 12 return false; 13 } 14 }
接口是用於主機名驗證,准確說是驗證服務器ca證書中的host是否和請求地址host一致,為什么要進行主機名驗證呢?其實目的是加強一層安全防護,防止惡意程序利用中間人攻擊。
什么是中間人攻擊?
假設有一個攻擊者處於“瀏覽器”和“網站服務器”的通訊線路之間(比如公共WIFI),它的攻擊過程如下:
- 服務器向客戶端發送公鑰。
- 攻擊者截獲公鑰,保留在自己手上。
然后攻擊者自己生成一個【偽造的】公鑰,發給客戶端。 - 客戶端收到偽造的公鑰后,生成加密hash值發給服務器。
- 攻擊者獲得加密hash值,用自己的私鑰解密獲得真秘鑰。
同時生成假的加密hash值,發給服務器。 - 服務器用私鑰解密獲得假秘鑰。
FIddler就是通過這種方式截獲HTTPS信息。
上面問題的根源是因為“缺乏身份認證機制”,需要驗證【偽造的】公鑰是否是網站服務器的,我們客戶端httpClient可以通過驗證公鑰中的host,防止被中間人攻擊。
參考資料: