在開發https應用時,你的測試服務器常常沒有一個(有效的)SSL證書。在你的客戶端連接測試服務器時,如下的異常會被拋出:”javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated”。
我將討論使用Apache HttpClient時,解決該問題的一種方法(http://hc.apache.org/httpcomponents-client/)。
1. 代碼片段
通常,你會像下面那樣來創建HttpClient:
我們將需要告訴client使用一個不同的TrustManager。TrustManager(http://download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/api/javax/net/ssl/TrustManager.html)是一個檢查給定的證書是否有效的類。SSL使用的模式是X.509(http://en.wikipedia.org/wiki/X.509),對於該模式Java有一個特定的TrustManager,稱為X509TrustManager。首先我們需要創建這樣的TrustManager。
下面我們需要找到一個將TrustManager設置到我們的HttpClient的方法。TrustManager只是被SSL的Socket所使用。Socket通過SocketFactory創建。對於SSL Socket,有一個SSLSocketFactory(http://download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/api/javax/net/ssl/SSLSocketFactory.html)。當創建新的SSLSocketFactory時,你需要傳入SSLContext到它的構造方法中。在SSLContext中,我們將包含我們新創建的TrustManager。
首先我們需要得到一個SSLContext:
TLS是SSL的繼承者,但是它們使用相同的SSLContext。
然后我們需要使用我們上面新創建的TrustManager來初始化該上下文:
最后我們創建SSLSocketFactory:
現在我們仍然需要將SSLSocketFactory注冊到我們的HttpClient上。這是在SchemeRegistry中完成的:
我們注冊了一個新的Scheme,使用協議https,我們新創建的SSLSocketFactory包含了我們的TrustManager,然后我們告訴HttpClienthttps的默認端口是443.
2. 完整示例代碼
下面的類接收HttpClient作為參數,然后返回一個新的接受任意SSL證書的HttpClient:
/** * 避免HttpClient的”SSLPeerUnverifiedException: peer not authenticated”異常 * 不用導入SSL證書 * @author shipengzhi(shipengzhi@sogou-inc.com) * */ public static class WebClientDevWrapper { public static org.apache.http.client.HttpClient wrapClient(org.apache.http.client.HttpClient base) { try { SSLContext ctx = SSLContext.getInstance("TLS"); X509TrustManager tm = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {} }; ctx.init(null, new TrustManager[] { tm }, null); SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("https", 443, ssf)); ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(registry); return new DefaultHttpClient(mgr, base.getParams()); } catch (Exception ex) { ex.printStackTrace(); return null; } } }
現在你就可以在代碼中如下的創建HttpClient了:
public HttpClient4() { this.client = new DefaultHttpClient(); this.client = WebClientDevWrapper.wrapClient(client); }