天天看點

FileDownLoader https 無法下載下傳問題(SSLHandshakeException)

1、問題:

項目下載下傳改為 https 協定,連接配接代理後使用 liulishuo 的 FileDownLoader 無法下載下傳,抛出異常如下:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

2、原因:

沒有信任證書或該域名的根證書不被信任

3、解決方案:

方案一:添加證書檔案,參考這篇部落格;

方案二:在程式中繞過證書校驗;

本文采用的是方案二,繞過證書校驗。具體步驟如下:

  1. 自定制 OkHttp3Connection 替換 FileDownload 的預設連接配接方式(FileDownloadUrlConnection)
  2. 在建立 OkHttpClient.Builder 的時候複寫 hostnameVerifier ,并建立TrustManager 忽略 SSL 驗證
  3. 使用FileDownloader.setupOnApplicationOnCreate(this).connectionCreator(....)的方式注冊 FileDownloader

FileDownLoader 初始化代碼:

public class FileDownloaderHelper {

    // 逾時設定 機關 秒
    private static final int READ_TIMEOUT = 5 * 60;
    private static final int WRITE_TIMEOUT = 5 * 60;
    private static final int CONNECT_TIMEOUT = 30;

    /**
    * 在主程序的 Application 的 onCreate 裡調用
    */
    public static void setup(Application application) {
        FileDownloader.setup(application);
    }

    /**
    * 在 FileDownLoader 程序的 Application 的 onCreate 裡調用,繞過 SSL 認證代碼如果不在 FileDownLoader 程序調用也無效
    */
    public static void initInDownloadProcess(Application application) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
		// 添加 SSL 認證
        SSLTrustManager.addVerify(builder);

        FileDownloader.setupOnApplicationOnCreate(application)
                .connectionCreator(new OkHttp3Connection.Creator(builder))// 自實作 OkHttp3Connection
                .commit();
    }
}
           

 看下如何繞過 SSL 認證,代碼如下:

public class SSLTrustManager {

    public static void addVerify(OkHttpClient.Builder builder) {
        if (builder == null) {
            return;
        }
        builder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                // 這裡是所有域名都繞過 SSL 驗證,最好是隻添加信任的域名
                return true;
            }
        });
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());
            builder.sslSocketFactory(sslContext.getSocketFactory(), xtm);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    private static X509TrustManager xtm = new X509TrustManager() {
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            X509Certificate[] x509Certificates = new X509Certificate[0];
            return x509Certificates;
        }
    };
}
           

OkHttp3Connection 實作如下:

public class OkHttp3Connection implements FileDownloadConnection {

    private OkHttpClient mClient;
    private Request.Builder mRequestBuilder;

    private Request mRequest;
    private Response mResponse;

    public OkHttp3Connection(String url, OkHttpClient client) {
        try {
            mRequestBuilder = new Request.Builder().url(url);
            mClient = client;
        } catch (Exception e) {
            LogUtil.e(e.getMessage());
        }
    }

    @Override
    public void addHeader(String name, String value) {
        mRequestBuilder.addHeader(name, value);
    }

    @Override
    public boolean dispatchAddResumeOffset(String etag, long offset) {
        return false;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        if (mResponse == null) throw new IllegalStateException("Please invoke #execute first!");
        return mResponse.body().byteStream();
    }

    @Override
    public Map<String, List<String>> getRequestHeaderFields() {
        if (mRequest == null) {
            mRequest = mRequestBuilder.build();
        }

        return mRequest.headers().toMultimap();
    }

    @Override
    public Map<String, List<String>> getResponseHeaderFields() {
        return mResponse == null ? null : mResponse.headers().toMultimap();
    }

    @Override
    public String getResponseHeaderField(String name) {
        return mResponse == null ? null : mResponse.header(name);
    }

    @Override
    public boolean setRequestMethod(String method) throws ProtocolException {
        return true;
    }

    @Override
    public void execute() throws IOException {
        if (mRequest == null) {
            mRequest = mRequestBuilder.build();
        }

        mResponse = mClient.newCall(mRequest).execute();
    }

    @Override
    public int getResponseCode() throws IOException {
        if (mResponse == null) throw new IllegalStateException("Please invoke #execute first!");

        return mResponse.code();
    }

    @Override
    public void ending() {
        mRequest = null;
        mResponse = null;
    }

    /**
     * The creator for the connection implemented with the okhttp3.
     */
    public static class Creator implements FileDownloadHelper.ConnectionCreator {

        private OkHttpClient mClient;
        private OkHttpClient.Builder mBuilder;

        public Creator() {
        }

        /**
         * Create the Creator with the customized {@code client}.
         *
         * @param builder the builder for customizing the okHttp client.
         */
        public Creator(OkHttpClient.Builder builder) {
            mBuilder = builder;
        }

        @Override
        public OkHttp3Connection create(String url) throws IOException {
            if (mClient == null) {
                synchronized (Creator.class) {
                    if (mClient == null) {
                        mClient = mBuilder != null ? mBuilder.build() : new OkHttpClient();
                        mBuilder = null;
                    }
                }
            }
            return new OkHttp3Connection(url, mClient);
        }
    }
}
           

大功告成!

參考連結:

https://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https/6378872#6378872

https://github.com/lingochamp/FileDownloader/issues/437