本章是HTTPS那些事兒的第三篇文章,其他相關(guān)文章請(qǐng)參見(jiàn):前言
本篇主要描述JAVA經(jīng)常遇到的場(chǎng)景:不校驗(yàn)服務(wù)器CA證書(shū)。主要包含以下內(nèi)容:
- HttpsURLConnection不校驗(yàn)服務(wù)器CA證書(shū)
- Spring RestTemplate不校驗(yàn)服務(wù)器CA證書(shū)
* 注意
本文純手工打造,轉(zhuǎn)載請(qǐng)注明出處。
HttpsURLConnection不校驗(yàn)服務(wù)器CA證書(shū)
有些情況下,雖然服務(wù)器端使用的是https協(xié)議,但是其證書(shū)不是由權(quán)威機(jī)構(gòu)頒發(fā)的,客戶端如果使用jdk默認(rèn)的證書(shū)會(huì)校驗(yàn)失敗。為了在項(xiàng)目初期進(jìn)行調(diào)試,我們可以忽略服務(wù)器證書(shū)校驗(yàn)。由前一篇文章可知,要達(dá)到不校驗(yàn)服務(wù)器證書(shū)的目的,必須將hostname校驗(yàn)和CA證書(shū)校驗(yàn)同時(shí)關(guān)閉。
要忽略hostname校驗(yàn),可以通過(guò)前一篇文章JAVA中HTTPS那些事兒中的HostnameVerifier小節(jié)介紹的case4場(chǎng)景來(lái)實(shí)現(xiàn)。要忽略證書(shū)校驗(yàn),則需要自定義SSLSocketFactory。整體代碼如下:
public void ignore() throws NoSuchAlgorithmException, KeyManagementException {
// 自定義證書(shū)校驗(yàn)器
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates,
String s) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates,
String s) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// 自定義hostname校驗(yàn)器
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}
從代碼中可以看到setDefaultSSLSocketFactory和setDefaultHostnameVerifier都是HttpsURLConnection的靜態(tài)方法,也就是此處的設(shè)置是全局的。所以在進(jìn)行實(shí)際的HTTPS訪問(wèn)前,調(diào)用一次ignore方法,即可在后續(xù)所有的HTTPS訪問(wèn)中達(dá)到不校驗(yàn)服務(wù)器CA證書(shū)的目的。
Spring RestTemplate不校驗(yàn)服務(wù)器CA證書(shū)
Spring中的RestTemplate是用于訪問(wèn)Restful服務(wù)的便捷工具類,該類依靠更下層的組件來(lái)完成網(wǎng)絡(luò)請(qǐng)求。默認(rèn)使用JDK中的HttpURLConnection,同時(shí)支持配置Apache HttpComponents HttpClient(4.3+)、OkHttp(2.x and 3.x)、Netty4。所以要使RestTemplate在訪問(wèn)HTTPS服務(wù)時(shí)忽略證書(shū)校驗(yàn),其實(shí)需要配置的是底層的實(shí)現(xiàn)組件。
- 如果使用默認(rèn)實(shí)現(xiàn)HttpURLConnection,如果要忽略證書(shū)只需要按照前一小節(jié)HttpsURLConnection不校驗(yàn)服務(wù)器CA證書(shū)進(jìn)行操作即可。
- Apache HttpComponents HttpClient(4.3+)不校驗(yàn)CA證書(shū)
Apache的HttpClient也是依賴于標(biāo)準(zhǔn)的java加密(Java Cryptography-JCE)和安全socket擴(kuò)展(Secure Sockets-JSEE),所以需要忽略證書(shū)同樣需要?jiǎng)?chuàng)建一個(gè)javax.net.ssl.SSLContext。與HttpsURLConnection一樣Apache HttpClient提供可選的HostnameVerifier功能,并提供兩個(gè)實(shí)現(xiàn)類DefaultHostnameVerifier和NoopHostnameVerifier,第一個(gè)采用RFC 2818規(guī)則校驗(yàn)hostname,第二個(gè)則不進(jìn)行校驗(yàn)。如果需要不校驗(yàn)服務(wù)器CA證書(shū),Apache HttpClient提供了一個(gè)幫助了可以直接使用,代碼如下:
public static void main(String[] args) throws IOException, KeyStoreException,
NoSuchAlgorithmException, KeyManagementException {
// 配置Apache HttpClient
HttpClient httpClient = HttpClients
.custom()
.setSSLContext(
new SSLContextBuilder()
.loadTrustMaterial(null, TrustAllStrategy.INSTANCE)
.build()
)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
// 讓RestTemplate使用Apache HttpClient訪問(wèn)網(wǎng)絡(luò)
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
String resp = restTemplate.getForObject(IgnoreServerCerts.testUrl,
String.class);
System.out.println(resp);
}
- 其他
RestTemplate同時(shí)支持使用OkHttp(2.x and 3.x)和Netty4,但是由于項(xiàng)目中暫未所用,我對(duì)這些組件也不熟悉,如果有使用到可以自行研究。
總結(jié)
本篇文章主要介紹的是java中怎么實(shí)現(xiàn)不校驗(yàn)HTTPS服務(wù)器證書(shū),主要介紹JDK中的HttpURLConnection和Apache HttpComponents HttpClient(4.3+)怎么忽略。同時(shí)明確Spring的RestTemplate是依賴于底層網(wǎng)絡(luò)訪問(wèn)組件實(shí)現(xiàn)的Http訪問(wèn),要忽略證書(shū)只需要配置底層組件即可。
至此,本系列文章主要部分已技術(shù),還有最后一篇可供選擇閱讀使用keytool模擬CA證書(shū)頒發(fā)過(guò)程。
本人程序猿一枚,語(yǔ)言能力有限,這個(gè)系列的文章是在工作過(guò)程中積累而來(lái)的,原意僅供自己備忘?,F(xiàn)公開(kāi)出來(lái),也僅是想幫助有需要的人,如有問(wèn)題,可隨時(shí)留言,如果我懂我會(huì)不定期回復(fù)。