【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;
    }
}

你可能感兴趣的:(Android,-,项目问题总结,Android,-,版本适配,Android,-,其它,android,ssl)