Android 網路框架之OKhttp實現https請求

Penny-聽海發表於2017-09-25

1.HTTPS定義

HTTPS全稱為Hyper Text Transfer Protocol over Secure Socket Layer或是Hypertext Transfer Protocol Secure


中文含義為“超文字傳輸安全協議” 。是以安全為目標的HTTP通道。簡單講是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL。 它是一個URI scheme(抽象識別符號體系),句法類同http:體系。用於安全的HTTP資料傳輸。https:URL表明它使用了HTTP,但HTTPS存在不同於HTTP的預設埠及一個加密/身份驗證層(在HTTP與TCP之間)。這個系統的最初研發由網景公司(Netscape)進行,並內建於其瀏覽器Netscape Navigator中,提供了身份驗證與加密通訊方法。現在它被廣泛用於全球資訊網上安全敏感的通訊,例如交易支付方面。

 

 

 

 

2.HTTPS和HTTP的區別

超文字傳輸協議HTTP協議被用於在Web瀏覽器和網站伺服器之間傳遞資訊。HTTP協議以明文方式傳送內容,不提供任何方式的資料加密,如果攻擊者擷取了Web瀏覽器和網站伺服器之間的傳輸報文,就可以直接讀懂其中的資訊,因此HTTP協議不適合傳輸一些敏感資訊,比如信用卡號、密碼等。


為了解決HTTP協議的這一缺陷,需要使用另一種協議:安全套接字層超文字傳輸協議HTTPS。為了資料傳輸的安全,HTTPS在HTTP的基礎上加入了SSL協議,SSL依靠證照來驗證伺服器的身份,併為瀏覽器和伺服器之間的通訊加密。


HTTPS和HTTP的區別主要為以下四點:


2.1.https協議需要到ca申請證照,一般免費證照很少,需要交費。
 

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

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

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

 

 

 

3.HTTPS的工作原理


HTTPS在傳輸資料之前需要客戶端(瀏覽器)與服務端(網站)之間進行一次握手,在握手過程中將確立雙方加密傳輸資料的密碼資訊。握手過程的簡單描述如下:

瀏覽器將自己支援的一套加密演算法、HASH演算法傳送給網站。
網站從中選出一組加密演算法與HASH演算法,並將自己的身份資訊以證照的形式發回給瀏覽器。證照裡面包含了網站地址,加密公鑰,以及證照的頒發機構等資訊。
瀏覽器獲得網站證照之後,開始驗證證照的合法性,如果證照信任,則生成一串隨機數字作為通訊過程中對稱加密的祕鑰。然後取出證照中的公鑰,將這串數字以及HASH的結果進行加密,然後發給網站。
網站接收瀏覽器發來的資料之後,通過私鑰進行解密,然後HASH校驗,如果一致,則使用瀏覽器發來的數字串使加密一段握手訊息發給瀏覽器。
瀏覽器解密,並HASH校驗,沒有問題,則握手結束。接下來的傳輸過程將由之前瀏覽器生成的隨機密碼並利用對稱加密演算法進行加密。

握手過程中如果有任何錯誤,都會使加密連線斷開,從而阻止了隱私資訊的傳輸。

 

 

 

 

4.程式碼實現

 

1.使用okhttp實現https請求

實現https我們需要一份ca證照,購買的證照,格式為.pfx,帶有公鑰和私鑰,附帶一個密碼。還有一種格式為.cer的證照,這種證照是沒有私鑰的。 


伺服器會將證照配置到tomcat中,客戶端則存放在本地,app啟動的時候載入進去。

 

.pfx格式和.cer格式的區別:

1.帶有私鑰的證照 
  由Public Key Cryptography Standards #12,PKCS#12標準定義,包含了公鑰和私鑰的二進位制格式的證照形式,以pfx作為證照檔案字尾名。 
   
2.二進位制編碼的證照 
  證照中沒有私鑰,DER 編碼二進位制格式的證照檔案,以cer作為證照檔案字尾名。

3.Base64編碼的證照 
證照中沒有私鑰,BASE64 編碼格式的證照檔案,也是以cer作為證照檔案字尾名。

 

 

2.程式碼

 

1.OkHttp設定

mOkHttpClient=new OkHttpClient.Builder()
                    .connectTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//連線時間
                    .readTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//讀時間
                    .writeTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//寫時間
                    .sslSocketFactory(sslSocketFactory,trustManager)//Https證照
                    .build();

即:.sslSocketFactory(sslSocketFactory,trustManager)//Https證照

通過sslSocketFactory方法設定https證照

 

 

2.獲取 SSLSocketFactory和 X509TrustManager 物件

 

2.1.下載相關證照放到asset目錄下

 

2.2.程式碼

public class MainApplication extends Application {

    private static SSLSocketFactory sslSocketFactory;
    private static X509TrustManager x509TrustManager;

    @Override
    public void onCreate() {
        super.onCreate();
        initx509TrustManagerAndsslSocketFactory();
    }

    /**
     * 初始化
     * */

    private void initx509TrustManagerAndsslSocketFactory(){
        InputStream inputStream = null;
        try {
            inputStream = getApplicationContext().getAssets().open("srca.cer"); //得到證照的輸入流
            try {
                if(null!=inputStream){
                    x509TrustManager = trustManagerForCertificates(inputStream);//以流的方式讀入證照
                    if(null!=x509TrustManager){
                        SSLContext sslContext = SSLContext.getInstance("TLS");
                        sslContext.init(null, new TrustManager[]{x509TrustManager}, null);
                        sslSocketFactory = sslContext.getSocketFactory();
                    }
                }
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null!=inputStream){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        MainApplication.setSslSocketFactory(sslSocketFactory);
        MainApplication.setX509TrustManager(x509TrustManager);
    }

    /**
     * 以流的方式新增信任證照
     */

    private 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");
        }

        // Put the certificates a key store.
        char[] password = "password".toCharArray(); // Any password will work.
        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];
    }

    /**
     * 新增password
     * @param password
     * @return
     * @throws GeneralSecurityException
     */

    private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // 這裡新增自定義的密碼,預設
            InputStream in = null; // By convention, 'null' creates an empty key store.
            keyStore.load(in, password);
            return keyStore;
        } catch (IOException e) {
            throw new AssertionError(e);
        }
    }

    public static SSLSocketFactory getSslSocketFactory() {
        return sslSocketFactory;
    }

    public static void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
        MainApplication.sslSocketFactory = sslSocketFactory;
    }

    public static X509TrustManager getX509TrustManager() {
        return x509TrustManager;
    }

    public static void setX509TrustManager(X509TrustManager x509TrustManager) {
        MainApplication.x509TrustManager = x509TrustManager;
    }
}

 

 

注:這裡只是測試,用的12306以前的證照。現在的12306已近不需要這個證照了,使用OkHttp不用配置sslSocketFactory也能訪問。

 

 

 

完整程式碼: https://github.com/wujianning/CustomOkHttps

相關文章