Android okhttp3.0配置https的自簽證書和信任所有證書

曉涵說發表於2019-03-09

1.Https相關知識

1.1 http與https的區別

HTTP協議傳輸的資料都是未加密的,也就是明文的,因此使用HTTP協議傳輸隱私資訊非常不安全,為了保證這些隱私資料能加密傳輸,於是網景公司設計了SSL(Secure Sockets Layer)協議用於對HTTP協議傳輸的資料進行加密,從而就誕生了HTTPS。簡單來說,HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,要比http協議安全。

HTTPS和HTTP的區別主要如下:

  • https協議需要到ca申請證照,一般免費證照較少,因而需要一定費用。

  • http是超文字傳輸協議,資訊是明文傳輸,https則是具有安全性的ssl加密傳輸協議。

  • http和https使用的是完全不同的連線方式,用的埠也不一樣,前者是80,後者是443。

  • http的連線很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,比http協議安全。

1.2 Https的優點

  • 認證使用者和伺服器,確保資料傳送到正確的客戶機和伺服器;(驗證證照)

  • 加密資料以防止資料中途被竊取;(加密)

  • 維護資料的完整性,確保資料在傳輸過程中不被改變。(摘要演算法)

2.Okhttp中使用自簽名證照

一般支援https的網站,都是CA(Certificate Authority)機構頒發的證照,但是一般該機構頒發的證照需要提供費用且有使用時間的限制,到期需要續費。否則預設該連結是不信任的,通過okHttp無法直接訪問。

但是我們可以使用自籤的方式,通過JDK自帶的keytool.exe 生成一個自己的證照,然後使用該證照內容。雖然也是會出現提示“不安全”,但是我們可以通過okhttp訪問連結。

2.1 使用自簽證照

將證照檔案放置在assets目錄(也可以放置在其他目錄下,只要能正確讀取到該檔案),在建立OkhttpClient物件時sslSocketFactory()將該證照資訊新增。

private SSLContext getSLLContext() {
        SSLContext sslContext = null;
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            InputStream certificate = mContext.getAssets().open("gdroot-g2.crt");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            String certificateAlias = Integer.toString(0);
            keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
            sslContext = SSLContext.getInstance("TLS");
            final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return sslContext;
    }
複製程式碼

通過以上程式碼可以看出,通過InputStream方式讀取證照資訊,因此為了避免將證照檔案打包到APK中,我們可以直接將證照檔案內容放置在String中,將該字串轉為流的形式。

在使用okhttp時,將其設定到sslSocketFactory中。

OkHttpClient httpClient = new OkHttpClient().newBuilder()
                .sslSocketFactory(getSLLContext().getSocketFactory())
                .build();
複製程式碼

2.2 信任所有證照

通過新增證照的形式,可以實現客戶端訪問Https服務端的功能,但是如果服務端更換證照內容,那麼客戶端需要相應的更換https證照,否則無法正常互動獲取不到資料,我們可以通過自定義X509TrustManager的形式實現來規避所有的證照檢測,實現信任所有證照的目的。

 private OkHttpClient getHttpsClient() {
        OkHttpClient.Builder okhttpClient = new OkHttpClient().newBuilder();
        //信任所有伺服器地址
        okhttpClient.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                //設定為true
                return true;
            }
        });
        //建立管理器
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] x509Certificates,
                    String s) throws java.security.cert.CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] x509Certificates,
                    String s) throws java.security.cert.CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[] {};
            }
        } };
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

            //為OkHttpClient設定sslSocketFactory
            okhttpClient.sslSocketFactory(sslContext.getSocketFactory());

        } catch (Exception e) {
            e.printStackTrace();
        }

        return okhttpClient.build();
    }
複製程式碼

建立X509TrustManager物件,並實現其中的方法,由於X509TrustManager是通用證照格式,只需要拿到該格式就行。最後init該安全協議,將其放入okhttp的sslSocketFactory中。 由於Retrofit只是對Okhttp網路介面的封裝,因此實際使用中,該方法同樣適用於Retrofit中。

相關文章