Java程序异常分析处理记录:使用OkHttp调用提示PKIX path building failed分析处理

背景:生产环境调用三方SDK所有请求提示异常

本地环境复现日志如下:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
	at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
	at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
	at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
	at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1506)
	at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
	at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
	at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
	at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:350)
	at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:308)
	at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:182)
	at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:238)
	at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:111)
	at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:79)
	at okhttp3.internal.connection.Transmitter.newExchange$okhttp(Transmitter.kt:163)
	at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:35)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
	at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:82)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
	at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:84)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
	at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:71)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:112)
	at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:87)
	at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.kt:194)
	at okhttp3.RealCall.execute(RealCall.kt:67)
	at com.xiaohongshu.fls.opensdk.client.OrderClient.execute(OrderClient.java:35)
	at com.ewp.datacenter.module.xiaohongshu.service.RedBookServiceImpl.main(RedBookServiceImpl.java:459)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
	at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
	at sun.security.validator.Validator.validate(Validator.java:260)
	at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
	at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
	at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
	at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1488)
	... 29 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:146)
	at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
	at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
	at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
	... 35 common frames omitted

本地复现环境:

// 本地系统
版本    Windows 11 专业版
版本号    24H2

// JDK版本(子版本严格限制)
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

// pom依赖
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.3.1</version>
        </dependency>

本地复现代码:

注意:代码块A和代码块B只能放开一个,另一个需要注释

代码块A为复现问题代码块

代码块B为下下策:关闭ssl证书验证

package edu.xq.test;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class OKHttpTest {

    public static final OkHttpClient okHttpClient;

    /**
     * 代码块A:创建一个默认的OkHttpClient实例
     */
    static {
        okHttpClient = (new OkHttpClient.Builder())
                .readTimeout(30L, TimeUnit.SECONDS)
                .writeTimeout(30L, TimeUnit.SECONDS)
                .connectTimeout(5L, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true).build();
    }

    /**
     * 代码块B:创建一个信任所有证书的OkHttpClient实例(PS:AI工具写代码确实好用)
     */
//    static {
//        // 创建信任所有证书的TrustManager
//        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[]{}; }
//                }
//        };
//
//        try {
//            SSLContext sslContext = SSLContext.getInstance("SSL");
//            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
//
//            okHttpClient = new OkHttpClient.Builder()
//                    .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager)trustAllCerts[0])
//                    .hostnameVerifier((hostname, session) -> true)
//                    .readTimeout(30L, TimeUnit.SECONDS)
//                    .writeTimeout(30L, TimeUnit.SECONDS)
//                    .connectTimeout(5L, TimeUnit.SECONDS)
//                    .retryOnConnectionFailure(true)
//                    .build();
//        } catch (NoSuchAlgorithmException | KeyManagementException e) {
//            throw new RuntimeException("Failed to create unsafe SSL context", e);
//        }
//    }


    public static void main(String[] args) throws Exception {
        RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "{}");
        Request httpRequest = (new Request.Builder()).url("https://ark.xiaohongshu.com/ark/open_api/v3/common_controller").post(body).build();
        Response openapiResponse = null;
        try {
            openapiResponse = okHttpClient.newCall(httpRequest).execute();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String responseBody = openapiResponse.body().string();
        System.out.println(responseBody);
    }
}

问题描述:

OkHttp调用JRE的SSL证书校验不通过

处理思路:

1、服务端看:那就看该网站的SSL证书配置是否正常,搜索分析问题发现有人提到

        1.1 证书是由 受信任的根证书颁发机构 颁发的;eg:自己生成etc.
        1.2 证书链的问题:服务端重新校验配置;

2、客户端看:那就看谁在校验,取消校验或者升级对于加密库

        2.1 JRE版本低,里面的证书库教旧:升级JRE加密库或JDK版本
        2.2 取消校验:生产不推荐哦

解决方案:

本测试样本已经排除服务端问题,故提供客户端处理方案

方案一:校验不通过,那就不校验,放开校验

参考 本地复现代码 注释代码块A 放开代码块B

方案二:替换依赖SDK中的class(其实也是方案一)

因为不是自己代码,So自己本地反编译好,替换SDK中的class即可。(操作同方法一)

方案三:升级jdk子版本(其他中间子版本没测试,原来本机就是这个版本切换就好了)

java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)

方案四:将证书放到本地,毕竟不是长久之计,测试学习或临时处理吧

1、将 目标网站的crt 证书[目标网站的crt 证书在本机的路径]导入到 Java 的默认证书库 cacerts 中,并给该证书指定了别名 [证书别名]。这使得 Java 在运行时可以信任该证书所代表的实体或服务器,常用于 SSL/TLS 连接等场景。(红字替换成自己的,keytool记得用jre的[jdk下好像也有个同名工具,记得区分]。j)

keytool -import -v -trustcacerts -alias [证书别名] -file [目标网站的crt 证书在本机的路径] -storepass changeit -keystore %JAVA_HOME%/jre/lib/security/cacerts

2、使用 keytool 工具,查询列出位于 %JAVA_HOME%\jre\lib\security\cacerts 的密钥库中的所有证书信息:

keytool -list -keystore %JAVA_HOME%\jre\lib\security\cacerts -storepass changeit

总结: 

综上所述,推荐对方案三进行实施,即升级到JDK更高的子版本。如果仍然遇到问题,可以考虑结合方案四,将相关证书导入到信任库中。同时,建议在生产环境中严格遵循SSL证书验证的最佳实践,确保系统安全性。希望这些建议能帮助您解决问题。如果您有更多疑问,请随时提出。​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

### 解决 Android 中 PKIX 路径构建失败问题 当遇到 `PKIX path building failed` 或者 `SunCertPathBuilderException: Unable to find valid certification path to requested target` 错误时,这通常意味着客户端无法验证服务器证书的有效性。以下是几种可能的原因以及解决方案: #### 1. 配置自定义信任库 如果应用程序尝试连接到使用自签名证书的服务器,则默认的信任库中不会包含该证书。可以通过创建一个新的 Java KeyStore (`jssecacerts`) 并将其配置为程序的信任库来解决问题[^2]。 ```java // 加载自定义的信任库文件 jssecacerts System.setProperty("javax.net.ssl.trustStore", "path/to/jssecacerts"); ``` #### 2. 更新 CA 根证书 有时设备上的根证书过期或者缺失也会引发此错误。确保 Android 设备已安装最新的操作系统更新和安全补丁包可以有效防止这种情况发生。 #### 3. 修改网络设置 某些情况下,特定类型的 Wi-Fi 网络可能会干扰 SSL/TLS 握手过程中的认证链解析工作。尝试切换不同的无线网络环境(比如从公共模式改为私有模式),甚至改用移动数据服务都可能是有效的临时措施之一[^3]。 #### 4. 使用 HTTP 客户端库处理异常情况 对于一些高级应用场景来说,在应用层面上捕获并适当地响应此类异常也是可行的方法。例如 OkHttp 支持通过定制化的 TrustManager 来绕过不被认可的中间CA节点。 ```kotlin val client = OkHttpClient.Builder() .sslSocketFactory(createUnsafeSslSocketFactory(), object : X509TrustManager { override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {} override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {} override fun getAcceptedIssuers(): Array<X509Certificate> { return arrayOf() } }) .hostnameVerifier { _, _ -> true } // 不推荐用于生产环境 .build() ``` 请注意上述方法仅适用于测试目的;在正式发布前应始终遵循最佳实践以保障通信的安全性和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值