一、前言
最近在对接腾讯会议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();