【Android】OkHttp3网络请求SSL证书验证问题绕过解决方案(包括Android 10及以上适配)

出现情况

当我们将之前封装的一套OKhttp的网络请求换成HTTPS的时候会发现,日志中会报出:

java.security.cert.CertPathValidatorException
Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

如下所示:

2021-11-19 09:16:12.140 7773-7773 W/System.err: Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:674)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:551)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:617)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:507)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:426)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:354)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:89)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:224)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.ConscryptFileDescriptorSocket.verifyCertificateChain(ConscryptFileDescriptorSocket.java:407)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.NativeSsl.doHandshake(NativeSsl.java:387)
2021-11-19 09:16:12.140 7773-7773 W/System.err:     at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:226)
2021-11-19 09:16:12.140 7773-7773 W/System.err: 	... 34 more
2021-11-19 09:16:12.141 7773-7773 W/System.err: Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
2021-11-19 09:16:12.141 7773-7773 W/System.err: 	... 47 more

一番摸索之后,网上给出常规解决方案是在 OkHttpClient.Builder 创建者中添加 sslSocketFactory()hostnameVerifier() 来忽略对SSL证书的验证,如下所示:


        okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(DEFAULT_CONNECT_TIME, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_WRITE_TIME, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_READ_TIME, TimeUnit.SECONDS)
                //注意此处
                .sslSocketFactory(MySSLSocketClient.getSSLSocketFactory(), MySSLSocketClient.X509)
                .hostnameVerifier(MySSLSocketClient.getHostnameVerifier())
                //
                .addInterceptor(chain -> {
                    Request original = chain.request();
                    Request request = original.newBuilder()
                            .headers(headersBuilder.build())
                            .method(original.method(), original.body())
                            .build();
                    return chain.proceed(request);
                })
                .build();

MySSLSocketClient


import java.security.SecureRandom;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class MySSLSocketClient {
    //获取这个SSLSocketFactory
    public static SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, getTrustManager(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //获取TrustManager
    public static TrustManager[] getTrustManager() {
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        //8.0之前返回null
                        return new X509Certificate[]{};
                    }
                }
        };
        return trustAllCerts;
    }

    //获取HostnameVerifier
    public static HostnameVerifier getHostnameVerifier() {
        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        };
        return hostnameVerifier;
    }
}

至此,常规操作已经可以解决问题了。
但是,对于Android开发者来说总所周知的一件事就是:版本适配!(手动心酸泪目…)
对于在Android 10及以上的设备上,会出现设备信任问题(个人理解命名),报错如:
Required method checkServerTrusted
Unable to extract the trust manager on Android10Platform 之类

解决方式

在 OkHttp 的GitHub 中的 issues 已经给出了具体的解决方案
就是把上面 MySSLSocketClient类中的 X509TrustManager 的实现类换成 X509ExtendedTrustManager 实现类即可。

最终 MySSLSocketClient 类如下:

import java.net.Socket;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;

public class MySSLSocketClient {

    //获取TrustManager
    public static X509ExtendedTrustManager X509 = new X509ExtendedTrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {

        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {

        }

        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {

        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {

        }

        @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];
        }
    };
    
    //获取这个SSLSocketFactory
    public static SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new TrustManager[]{X509}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    //获取HostnameVerifier
    public static HostnameVerifier getHostnameVerifier() {
        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        };
        return hostnameVerifier;
    }
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
OkHttp3 中,可以通过配置 OkHttpClient 实例的 SSLSocketFactory 和 TrustManager 来实现绕过 SSL 验证,具体如下: ```java // 创建一个信任所有证书的 TrustManager TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}; // 创建一个 SSLContext,并使用上面的 TrustManager 初始化 SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, trustAllCerts, new SecureRandom()); // 创建一个 OkHttpClient 实例,并设置 SSLContext OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0]) .hostnameVerifier((hostname, session) -> true) .build(); // 发送请求 Request request = new Request.Builder() .url("https://example.com") .build(); Response response = client.newCall(request).execute(); ``` 在上面的代码中,我们创建了一个 TrustManager 实例,用于信任所有证书。然后使用这个 TrustManager 初始化一个 SSLContext,最后将这个 SSLContext 设置到 OkHttpClient 实例中。 需要注意的是,绕过 SSL 验证可能会带来安全风险,应该尽量避免在生产环境中使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MrRobot_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值