一、前言
最近在對接騰訊會議API接口,在鑒權完成后開始調用對方的接口,在此過程中出現調用報錯:javax.net.ssl.SSLHandshakeException。
二、出現原因
當你在進行https請求時,JDK中不存在三方服務的信任證書,導致出現錯誤javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX路徑構建失敗。
三、解決方法
1、獲取根證書安裝證書到你的JRE的Java cacerts中(安裝證書到PATHTOYOURJDK/JRE/lib目錄/ cacerts中)。
2、忽略SSL證書的校驗。
這里因為很多情況沒有證書,所以采用第二種方案,在你的代碼中進行忽略SSL證書校驗。
四、代碼
這里要區分你使用的是那種方式調用三方服務(RestTemplate 、OkHttpClient)。
1、RestTemplate
package com.hikvision.meeting.config; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import javax.net.ssl.*; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** * @author dongliang7 * @projectName * @ClassName Config2RestTemplate.java * @description: 跳過證書效驗 * @createTime 2021年11月23日 09:59:00 */ @Configuration public class Config2RestTemplate { @Bean public RestTemplate restTemplate() throws Exception { TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom() .loadTrustMaterial(null, acceptingTrustStrategy) .build(); // SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(createIgnoreVerifySSL(), // 指定TLS版本 null, // 指定算法 null, // 取消域名驗證 new HostnameVerifier() { @Override public boolean verify(String string, SSLSession ssls) { return true; } }); CloseableHttpClient httpClient = HttpClients.custom() .setSSLSocketFactory(csf) .build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient); requestFactory.setReadTimeout(60 * 1000);// ms requestFactory.setConnectTimeout(60 * 1000);// ms // 該代碼的意思是請求工廠類是否應用緩沖請求正文內部,默認值為true,當post或者put大文件的時候會造成內存溢出情況,設置為false將數據直接流入底層HttpURLConnection requestFactory.setBufferRequestBody(false); RestTemplate restTemplate = new RestTemplate(requestFactory); return restTemplate; } /** * 跳過證書效驗的sslcontext * * @return * @throws Exception */ private static SSLContext createIgnoreVerifySSL() throws Exception { SSLContext sc = SSLContext.getInstance("TLS"); // 實現一個X509TrustManager接口,用於繞過驗證,不用修改里面的方法 X509TrustManager trustManager = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; sc.init(null, new TrustManager[] { trustManager }, null); return sc; } }
2、OkHttpClient
package com.tencent.wemeet.gateway.restapisdk.util; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import javax.net.ssl.*; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.concurrent.TimeUnit; /** * @author dongliang7 * @projectName tenxun-meeting-api * @ClassName SSLSocketClient.java * @description: 創建 OkHttpClient 不進行SSL(證書)驗證 * @createTime 2021年11月19日 09:50:00 */ @Slf4j public class SSLSocketClient { public static OkHttpClient getUnsafeOkHttpClient() { try { // 創建不驗證證書鏈的信任管理器 final TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {} @Override public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {} @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[]{}; } } }; if (trustAllCerts.length != 1 || !(trustAllCerts[0] instanceof X509TrustManager)) { throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustAllCerts)); } X509TrustManager x509TrustManager = (X509TrustManager) trustAllCerts[0]; // 安裝全信任信任管理器 final SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); // 使用我們完全信任的管理器創建 ssl 套接字工廠 final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); OkHttpClient.Builder builder = new OkHttpClient.Builder() .connectTimeout(60 , TimeUnit.SECONDS).readTimeout(60 , TimeUnit.SECONDS).writeTimeout(120 , TimeUnit.SECONDS); builder.sslSocketFactory(sslSocketFactory , x509TrustManager); builder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); OkHttpClient okHttpClient = builder.build(); return okHttpClient; } catch (Exception e) { log.error("創建OkHttpClient不進行SSL(證書)驗證失敗:{}", e.getMessage()); throw new RuntimeException(e); } } }
獲取OkHttpClient :
//創建 OkHttpClient 不進行SSL(證書)驗證 private static final OkHttpClient okHttpClient = SSLSocketClient.getUnsafeOkHttpClient();