HTTPS工做原理以及Android中如何防止抓包

1. HTTPS的定義

說道HTTPS,不得不提HTTP,HTTP最大的缺陷就是明文傳輸,數據傳輸過程當中很容易被篡改,因此美國網景公司提出來HTTPS協議,相對HTTP,HTTPS多了一個S,這個S,其實就是SSL/TSL,SSL全稱安全套接字層,TSL1.0(傳輸層安全協議)是SSL3.0的升級版,是用於服務器和客戶端加密通訊的,因此能夠認爲二者是同一種協議,SSL由於自身的不安全性,在Android8.0已經被棄用了,以上能夠看出HTTPS=HTTP+SSL/TLSjava

2. 工做原理

1.HTTPS發起SSL鏈接,連接到服務器的443端口算法

2.服務器向客戶端發送公鑰和數字證書spring

3.客戶端經過隨機算法生成私鑰,而後經過服務器公鑰對該私鑰加密,生成對稱密鑰瀏覽器

4.客戶端向服務器發送對稱密鑰安全

5.服務器經過對稱密鑰對數據進行加密bash

6.客戶端經過對稱密鑰來對數據解密服務器

那問題來了,在第二步,若是有好事者截獲了服務器對客戶端發送的公鑰,而後僞形成服務器與客戶端通訊,這可如何是好呢,如何判斷該公鑰是合法的呢,數字證書就排上用場了網絡

3. 數字證書

數字證書是由CA簽發,全世界權威的CA一共100多個,數字證書裏包含一對非對稱密鑰,公鑰和私鑰以及頒發給、頒發者等信息,裏面的公鑰對服務器端傳輸的公鑰進行加密,生成密文,而後由客戶端的數字證書裏的私鑰進行解密,從而得到服務器的公鑰並確認該公鑰是合法的工具

以瀏覽器的www.baidu.com爲例,經過點擊連接旁的鎖標誌來打開數字證書ui

證書路徑展現的是證書鏈,根證書的權利最大,依次爲根證書>A>B>C,若是www.baidu.com 信任了根證書,則意味着A、B、C都信任

經過詳細信息能夠看到公鑰、公鑰參數、證書策略等證書的詳細信息 手機做爲訪問網絡的客戶端,固然也會有內置證書,以小米手機爲例,經過設置-更多設置-系統安全-加密與憑據-信任的憑據來打開內置的CA證書

系統證書是內置的, 我的證書是自定義的,好比charles的抓包證書是放在 我的證書下面的,那Android是如何發起HTTPS請求的呢?

4. 抓包的原理

日常使用的抓包工具,不管是fidder和charles都能解析客戶端和服務器的HTTPS數據,是如何作到的呢?其實抓包工具就充當了一箇中間人代理的角色,參照2.https的工具原理,抓包的工做原理以下:

  • 截獲客戶端向發起的HTTPS請求,佯裝客戶端,向真實的服務器發起請求
  • 截獲真實服務器的返回,佯裝真實服務器,向客戶端發送數據
  • 獲取了用來加密服務器公鑰的非對稱祕鑰和用來加密數據的對稱祕鑰

5. Android 中使用 HTTPS以及如何防止抓包

Android中如何訪問HTTPS呢,其實Retrofit、OkHttp均支持HTTPS的訪問 項目中引入網絡庫,以**implementation 'com.squareup.okhttp3:okhttp:4.2.0'**爲例,

final OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
final Request request = new Request.Builder()
        .url("https://www.baidu.com/robots.txt")
        .build();
final Response execute = okHttpClient.newCall(request).execute();
final String bodyStr = execute.body().string();
Log.d(TAG, bodyStr);
複製代碼

那若是關閉客戶端的CA證書,GlobalSign Root CA-R1,至關於不信任百度服務器的數字證書,會致使報錯

Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. 經過手動將GlobalSign Root CA-R1.cer放入項目中的assets文件夾,則可避免這一錯誤,如何引用項目中集成的證書呢? 經過

SSLContext sslContext;
        try {
            InputStream inputStream = getAssets().open("");
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{OkhttpU.trustManagerForCertificates(inputStream)}, null);
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, OkhttpU.trustManagerForCertificates(inputStream)).build();
            final Request request = new Request.Builder()
                    .url("https://www.baidu.com/robots.txt")
                    .build();
            final Response execute = okHttpClient.newCall(request).execute();
            final String bodyStr = execute.body().string();
            Log.d(TAG, bodyStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
複製代碼
public class OkhttpU {

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

        // 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];
    }

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

複製代碼

就能夠正常訪問https了

綜上,經過引入自定義證書,而後給OkHttp設置sslSocketFactory能夠有效的防止抓包,可是.cer放到assets下很容易被反編譯,能夠經過jdk下的命令keytool -printcert -rfc -file srca.cer導出字符串,而後經過

OkHttpClientManager.getInstance()
                .setCertificates(new Buffer()
                        .writeUtf8(CER_STRING) //CER_STRING是處處的string常量
                        .inputStream());
複製代碼

相關文章

相關文章
相關標籤/搜索