1、問題:
項目下載下傳改為 https 協定,連接配接代理後使用 liulishuo 的 FileDownLoader 無法下載下傳,抛出異常如下:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
2、原因:
沒有信任證書或該域名的根證書不被信任
3、解決方案:
方案一:添加證書檔案,參考這篇部落格;
方案二:在程式中繞過證書校驗;
本文采用的是方案二,繞過證書校驗。具體步驟如下:
- 自定制 OkHttp3Connection 替換 FileDownload 的預設連接配接方式(FileDownloadUrlConnection)
- 在建立 OkHttpClient.Builder 的時候複寫 hostnameVerifier ,并建立TrustManager 忽略 SSL 驗證
- 使用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