OkHttp配置HTTPS訪問,核心爲如下三個部分:html
第一個是ssl套接字工廠,第二個用來驗證主機名,第三個是證書信任器管理類.經過OkHttp實現HTTPS訪問須要本身實現以上三部分.另外還簡單說起了服務器端的部署,用的是Tomcat9,最後是一些常見問題的可能解決方案.前端
OkHttp是一款開源的處理網絡請求的輕量級框架,有Square公司貢獻,用於替代HttpUrlConnection與Apache HttpClient,目前Github上有36.4k的star.優勢有java
總的來講OkHttp是一款支持get/post請求,支持文件上傳/下載的優秀的HTTP框架.git
什麼?都沒有?買!
固然證書能夠不用買,可使用openssl之類的工具生成,不過自簽名的證書後面驗證的時候會有點麻煩,建議仍是購買.github
public static String test() { OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts()) .hostnameVerifier(new TrustAllHostnameVerifier()).build(); String url = "https://xxxxxxx"; //修改爲本身的url Request request = new Request.Builder().url(url).build(); Call call = build.newCall(request); Response response = call.execute(); if(response.body() != null) { String result = response.body().string(); //處理result } } private static class TrustAllCerts implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];} } private static class TrustAllHostnameVerifier implements HostnameVerifier { public boolean verify(String hostname, SSLSession session) { return true; } } private static SSLSocketFactory createSSLSocketFactory() { SSLSocketFactory ssfFactory = null; try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom()); ssfFactory = sc.getSocketFactory(); } catch (Exception e) { e.printStackTrace(); } return ssfFactory; }
這是一種暴力的方案,看類名就知道了,信任全部的證書與主機:web
public boolean verify(String hostname, SSLSession session) { return true; }
這個方法直接返回true,也就是信任全部的主機.後端
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
這裏兩個check函數沒有作任何的工做,表示接受任意的客戶端與服務端的證書.這樣寫的話至關因而使用了一個沒用的TrustManager,這樣還不如不加密,不推薦使用.數組
從兩方面入手修改,一是從X509TrustManager入手,二是從HostnameVerifier入手.緩存
先說個簡單的,這裏主要是驗證主機名,簡單的話,能夠以下實現:tomcat
HostnameVerifier hnv = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { if("www.test.com".equals(hostname)){ return true; } else { HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); return hv.verify(hostname, session); } } };
這裏驗證主機名是www.test.com就返回true,實現得比較簡單,業務複雜的話能夠結合配置中心,黑/白名單等動態校驗.
這裏其實有兩種方式,一種是以流的方式添加信任證書:
private static X509TrustManager trustManagerForCertificates(InputStream in) throws GeneralSecurityException { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in); if (certificates.isEmpty()) { throw new IllegalArgumentException("expected non-empty set of trusted certificates"); } char[] password = "password".toCharArray(); // 這裏可使用任意密碼 KeyStore keyStore = newEmptyKeyStore(password); int index = 0; for (Certificate certificate : certificates) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificate); } // Use it to build an X509 trust manager. KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, password); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); } return (X509TrustManager) trustManagers[0]; }
完整代碼見文末.這裏把工具類的方法實現成了靜態,調用時能夠直接:
OKHTTP.send("https://xxxxx");
另外一種方式是直接自定義一個TrustManager,重寫裏面的三個方法:
SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new TrustManager[]{ new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException {} @Override public void checkServerTrusted(X509Certificate[] chain,String authType) throws CertificateException { for (X509Certificate cert : chain) { // Make sure that it hasn't expired. cert.checkValidity(); // Verify the certificate's public key chain. try { cert.verify(((X509Certificate) ca).getPublicKey()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } catch (SignatureException e) { e.printStackTrace(); } } } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }, null);
第一個方法爲
@Override public void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException {}
該方法檢查客戶端的證書,因爲不須要對客戶端進行認證,默認便可.
第二個方法爲
@Override public void checkServerTrusted(X509Certificate[] chain,String authType)
該方法檢查服務器的證書,若不信任該證書則拋出異常,經過本身實現該方法能夠信任任何本身指定的證書,不作任何處理的話,不會拋出任何異常,至關於信任全部證書.這裏檢查了證書是否過時以及證書的簽名是否匹配.
第三個方法爲
@Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
返回受信任的X509證書數組.
這種方法筆者沒有試過,僅供參考.
服務器用的是Tomcat,簡單介紹一下部署.
後端處理用的Spring Boot的工程,就不演示了,使用IDEA打成war包後上傳到webapps下便可.
重點說一下Tomcat的配置,首先須要一個域名,修改conf/server.xml文件,找到默認的名叫localhost的Host:
而後直接複製Host標籤,把name修改爲本身的域名便可.
而後是證書的配置,筆者的證書在某某雲上購買的,這裏提供了幾種格式的證書下載:
Tomcat的是兩個文件,一個是pfx文件,一個是密碼文件,把pfx文件上傳到服務器的Tomcat後,繼續修改server.xml,大約87行左右的位置(Tomcat版本9.0.33):
修改以下:
添加了scheme,secure,keystoreFile,keystoreType,keystorePass,clientAuth,sslProtocol配置,同時去掉裏面的<SSLHostConfig>,keystoreFile是剛纔的pfx文件,採用絕對路徑,keystorePass是密碼.
另外默認的端口爲8443,這裏修改爲了8123.
重啓Tomcat後輸入
https://www.test.com:port
進行測試
這樣就成功了.
這個由於沒有完整的Demo很難作驗證,具體來講前端用的OkHttp核心都介紹了,後端的話服務器Tomcat也介紹了,用Spring Boot作個Demo應該不難.
這裏只給出了工具類OKHTTP的源碼:
github
1.蘋果核 - Android App 安全的HTTPS 通訊
2.Android OkHttp實現HTTPS訪問,支持Android 4.X系統HTTPS訪問
3.Android使用OkHttp請求自簽名的https網站