Android HTTPS從概念到編碼

HTTPS請求爲何就安全?

https是一個創建在密碼學基礎之上的一種安全通訊協議,準確來講是基於HTTP協議和SSL/TSL的組合,要想理解HTTPS必須瞭解密碼學的一些相關基礎概念。java

公鑰密碼體制

公鑰密碼體制分爲三個部分,公鑰,私鑰,加密解密算法,加密解密過程以下:android

  • 加密:經過加密算法和公鑰對內容(或者說明文)進行加密,獲得密文。加密過程須要用到公鑰。
  • 解密:經過解密算法和私鑰對密文進行解密,獲得明文。解密過程須要用到解密算法和私鑰。注意,由公鑰加密的內容,只能由私鑰進行解密,也就是說,由公鑰加密的內容,若是不知道私鑰,是沒法解密的

對稱加密算法

在對稱加密算法中,加密使用的密鑰和解密使用的密鑰是相同的。也就是說,加密和解密都是使用的同一個密鑰。所以對稱加密算法要保證安全性的話,密鑰要作好保密,只能讓使用的人知道,不能對外公開。這個和上面的公鑰密碼體制有所不一樣,公鑰密碼體制中加密是用公鑰,解密使用私鑰,而對稱加密算法中,加密和解密都是使用同一個密鑰,不區分公鑰和私鑰。 密鑰,通常就是一個字符串或數字,在加密或者解密時傳遞給加密/解密算法。前面在公鑰密碼體制中說到的公鑰、私鑰就是密鑰,公鑰是加密使用的密鑰,私鑰是解密使用的密鑰。算法

RSA簡介

RSA密碼體制是一種公鑰密碼體制,公鑰公開,私鑰保密,它的加密解密算法是公開的。 由公鑰加密的內容能夠而且只能由私鑰進行解密,而且由私鑰加密的內容能夠而且只能由公鑰進行解密。也就是說,RSA的這一對公鑰、私鑰均可以用來加密和解密,而且一方加密的內容能夠由而且只能由對方進行解密。瀏覽器

非對稱加密算法

在非對稱加密算法中,加密使用的密鑰和解密使用的密鑰是不相同的。前面所說的公鑰密碼體制就是一種非對稱加密算法,他的公鑰和是私鑰是不能相同的,也就是說加密使用的密鑰和解密使用的密鑰不一樣,所以它是一個非對稱加密算法。安全

瞭解了以上的基礎概念以後咱們就能夠聊聊爲何安全了,這裏咱們就能夠籠統的講一下當咱們打開一個https連接的時候發生了什麼事情。bash

  1. 當咱們在通訊過程開始時,會收到服務器的證書。
  2. 首先應用程序讀取證書中的Issuer(發佈機構),而後會在應用程序的系統中受信任的發佈機構的證書中去找。
  3. 若是找不到,那證書可能有問題,程序會給出一個錯誤信息。
  4. 若是在系統中找到這個證書的發佈機構,那麼應用程序就會從證書中取出這個機構證書的公鑰。
  5. 而後對服務器下發的證書裏面的指紋和指紋算法用這個公鑰進行解密,而後使用指紋算法計算服務器下發證書中的指紋。將這個計算的指紋與放在證書中的指紋對比。(非對稱加密
  6. 若是一致說明服務器下發的證書是經過經過機構發佈的。而後就能夠保證通訊安全了。
  7. 而後這時候應用程序選擇一個對稱加密算法和一個祕鑰,用公鑰加密以後發送給服務器。
  8. 服務器收到後使用私鑰解密,獲取祕鑰,本次通話會從如今開始,經過對稱加密算法進行通訊。

經過上訴簡單的描述,也可瞭解到https通訊是是經過各類加密算法及證書比對校驗完成的一個http通訊。服務器

SSL/TLS證書?

ssl Secure Sockets Layer,如今應該叫"TLS",但因爲習慣問題,咱們仍是叫"SSL"比較多,ssl證書是數字證書的一種,數字證書有較多的內容項,裏面的內容比較多——Version、Serial number、Signature algorithm 等等,挑幾個重要的解釋一下。網絡

  • Issuer (證書的發佈機構)
    指出是什麼機構發佈的這個證書,也就是指明這個證書是哪一個公司建立的(只是建立證書,不是指證書的使用者)
  • Valid from , Valid to (證書的有效期)
    也就是證書的有效時間,或者說證書的使用期限。 過了有效期限,證書就會做廢,不能使用了。
  • Public key (公鑰)
    這個咱們在前面介紹公鑰密碼體制時介紹過,公鑰是用來對消息進行加密的
  • Subject (主題)
    這個證書是發佈給誰的,或者說證書的全部者,通常是某我的或者某個公司名稱、機構的名稱、公司網站的網址等。 對於這裏的證書來講,證書的全部者是Trustwave這個公司。
  • Signature algorithm (簽名所使用的算法) 就是指的這個數字證書的數字簽名所使用的加密算法,這樣就可使用證書發佈機構的證書裏面的公鑰,根據這個算法對指紋進行解密。指紋的加密結果就是數字簽名。
  • Thumbprint, Thumbprint algorithm (指紋以及指紋算法)
    這個是用來保證證書的完整性的,也就是說確保證書沒有被修改過。 其原理就是在發佈證書時,發佈者根據指紋算法(一個hash算法)計算整個證書的hash值(指紋)並和證書放在一塊兒,使用者在打開證書時,本身也根據指紋算法計算一下證書的hash值(指紋),若是和剛開始的值對得上,就說明證書沒有被修改過,由於證書的內容被修改後,根據證書的內容計算的出的hash值(指紋)是會變化的。 注意,這個指紋會使用"SecureTrust CA"這個證書機構的私鑰用簽名算法(Signature algorithm)加密後和證書放在一塊兒。

證書頒發機構(CA)

·Symantec (which bought VeriSign's SSL interests and owns Thawte and Geotrust) 38.1% 市場份額app

·Comodo SSL 29.1%框架

·Go Daddy 13.4%

·GlobalSign 10%

安卓系統中, 你能夠在下列路徑中找到證書頒發機構: 設置 -> 安全 -> 受信任的憑證

最佳和最安全的私鑰獲取方法是向可信任的證書頒發機構 (CA)(例如將帶您完成身份驗證過程的賽門鐵克公司)申請一個證書。一旦有了本身的證書,就能夠生成您本身的私鑰。您可使用該私鑰爲您的可執行文件或軟件庫簽名,私鑰僅可經過可追溯到 CA 的公鑰解鎖,而公鑰已預安裝在大部分瀏覽器中。若是在簽名後代碼被篡改,公鑰將不能覈實私鑰簽名的真實性,瀏覽器將當即向任未嘗試下載代碼的人彈出警告窗口。若是代碼無缺無損,則將提供您的文。

什麼是自簽名證書?

自簽名證書就是沒有經過受信任的證書頒發機構, 本身給本身頒發的證書.

SSL 證書大體分三類:

  • 由安卓承認的證書頒發機構(如: VeriSign), 或這些機構的下屬機構頒發的證書.
  • 沒有獲得安卓承認的證書頒發機構頒發的證書.
  • 本身頒發的證書, 分臨時性的(在開發階段使用)或在發佈的產品中永久性使用的兩種.

X.509 又是什麼

X.509 - 這是一種證書標準,主要定義了證書中應該包含哪些內容.其詳情能夠參考RFC5280,SSL/TLS使用的就是這種證書標準.

編碼格式

一樣的X.509證書,可能有不一樣的編碼格式,目前有如下兩種編碼格式.

PEM - Privacy Enhanced Mail,打開看文本格式,以"-----BEGIN..."開頭, "-----END..."結尾,內容是BASE64編碼. 查看PEM格式證書的信息:openssl x509 -in certificate.pem -text -noout Apache和*NIX服務器偏向於使用這種編碼格式.

DER - Distinguished Encoding Rules,打開看是二進制格式,不可讀. 查看DER格式證書的信息:openssl x509 -in certificate.der -inform der -text -noout Java和Windows服務器偏向於使用這種編碼格式.

KeyStore

在 Android 和 java中 更多的是使用keystore。最經常使用的工具就是keytool. Keytool是一個Java數據證書的管理工具 ,Keytool將密鑰(key)和證書(certificates)存在一個稱爲keystore的文件中。 在keystore裏,包含兩種數據:

  1. 密鑰實體(KeyEntry)——密鑰(secret key)
  2. 可信任的證書實體(CertEntry)——只包含公鑰

每一個keystore都關聯這一個獨一無二的alias,這個alias一般不區分大小寫

Android 中如何配置SSL/TLS

以上所述皆是對HTTPS SSL/TLS的概念敘述,經過這個描述會讓你對下面的配置以及編碼更加了解。咱們在Android 開發中配置項目支持HTTPS這是個基本的操做。下面我會以OkHttp網絡框架爲例。經過如下三點可完成項目配置。

  1. 如何配置系統、用戶信任證書校驗?
  2. 自簽名證書如何在項目中配置?
  3. 如何經過配置文件進行配置SSL/TLS證書?

如何配置系統、用戶信任證書校驗?

Android OkHttp框架默認是提供了對系統用戶信任證書的校驗。若是服務器下發的證書是被Android信任機構頒發的證書是不會出現安全提示的,或者是用戶添加到系統的證書也是不會出現任何安全提示。

在 AOSP 源碼庫中,CA 根證書主要存放在 system/ca-certificates 目錄下,而在 Android 系統中,則存放在 /system/etc/security/ 目錄下。因此咱們沒有Root的手機是沒法進行添加根證書的。

也能夠根據如下配置進行系統根證書安全校驗

public static SSLSocketFactory getSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;
        SSLContext sc = null;
        try {
            sc = SSLContext.getInstance("TLS");
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init((KeyStore) null);

            sc.init((KeyManager[]) null,  trustManagerFactory.getTrustManagers(), new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        return ssfFactory;
    }
複製代碼

特定的 CA 證書如何配置?

不管是權威機構頒發的證書仍是自簽名的,打包一份到 app 內部,好比存放在 asset 裏,下面的示例展現了此過程:從 InputStream 獲取一個特定的 CA,用該 CA 建立 KeyStore,而後用後者建立和初始化 TrustManager。TrustManager 是系統用於驗證來自服務器的證書的工具,可使用一個或多個 CA 從 KeyStore建立,而建立的 TrustManager 將僅信任這些 CA。

// Load CAs from an InputStream
    // (could be from a resource or ByteArrayInputStream or ...)
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    // From https://www.washington.edu/itconnect/security/ca/load-der.crt
    InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
    Certificate ca;
    try {
        ca = cf.generateCertificate(caInput);
        System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
    } finally {
        caInput.close();
    }

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);

    // Create a TrustManager that trusts the CAs in our KeyStore
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, tmf.getTrustManagers(), null);
複製代碼

以上代碼,能夠解決證書信任問題。但同時須要注意的是,這裏是基於Android默認的信任檢查來解決的。由於咱們沒有對TrustManager作任何修改,若是仍然遇到證書校驗不經過的狀況,則須要從新實現TrustManager,請用如下代碼代替「tmf.getTrustManagers()」:

。這時候咱們須要經過自定義的校驗方式方可完成校驗。經過自定義X509TrustManager來實現checkServerTrusted的校驗,其實就是經過上訴介紹的SSL/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);
複製代碼

先校驗證書的日期是否過時,其次經過本地預置證書的公鑰對服務器下發的數字證書進行對比校驗。

如何經過文件配置證書?

Android P新特性,全面禁止了非安全的http鏈接,若是要使用非加密鏈接,須要配置network security config

步驟以下:

  • 在res/xml下創建咱們本身的network security config文件,名字任意,能夠叫作network_security_config.xml
  • 若是咱們相對某些網址使用非安全鏈接,可使用以下配置,如下能夠說涵蓋了不少配置。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    //用於輔助抓包,7.0以上證書的權限變了
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            //信任用戶證書
            <certificates src="user"></certificates>

            //信任用戶本身安裝的證書
            <certificates src="system"></certificates>

            //定義白名單
            <certificates src="@raw/cn_area"></certificates>
        </trust-anchors>
    </base-config>

<!--    使用自簽名 SSL 證書的主機 配置-->
<!--    cleartextTrafficPermitted="false" 強制校驗https 不然能夠容許經過http-->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">example.com</domain>  <!-- 過濾域名,可配置多個 -->
        <domain includeSubdomains="true">example1.com</domain>  <!-- 通常會將 CDN 配置在此 -->
        <trust-anchors>
            <!--這就是咱們本身的證書-->
            <!--若是要信任多個證書,就能夠寫多個-->
            <certificates src="@raw/cn_area"/>
            <!--也能夠把這些證書放在一個目錄下-->
            <certificates src="@raw/cn_area"/>

            //信任用戶證書
            <certificates src="user"></certificates>

            //信任用戶本身安裝的證書
            <certificates src="system"></certificates>

        </trust-anchors>
<!--        固定證書-->
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
        </pin-set>

    </domain-config>

<!--    debug 模式配置調試CA-->
    <debug-overrides>
        <trust-anchors>
            //信任用戶證書
            <certificates src="user"></certificates>

            //信任用戶本身安裝的證書
            <certificates src="system"></certificates>
        </trust-anchors>
    </debug-overrides>

</network-security-config>

複製代碼
下面方式能夠經過具體的需求進行具體的配置

若是咱們的某個網址的證書是自簽名的證書,咱們想要訪問這個網址,能夠進行以下配置

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <!--這就是咱們本身的證書-->
            <!--若是要信任多個證書,就能夠寫多個-->
            <certificates src="@raw/my_ca"/>
            <!--也能夠把這些證書放在一個目錄下-->
            <certificates src="@raw/trusted_roots"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

複製代碼

若是咱們想讓App信任除系統以外的其餘的CA,能夠進行以下配置

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
          <!--在base-config中配置額外的CA-->
            <certificates src="@raw/extracas"/>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>
複製代碼

若是咱們在調試的時候staging服務器不是正規CA,咱們能夠進行以下配置

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <!--debug-overrides表示在調試狀態下信任的CA,當android:debugable爲true的時候,是調試狀態-->
    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_cas"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>
複製代碼

通常狀況下,應用信任全部預裝 CA。若是有預裝 CA 簽發欺詐性證書,則應用將面臨被中間人攻擊的風險。有些應用經過限制信任的 CA 集或經過固定證書,選擇限制其接受的證書集

<?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <domain-config>
            <domain includeSubdomains="true">example.com</domain>
            <pin-set expiration="2018-01-01">
                <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
                <!-- backup pin -->
                <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
            </pin-set>
        </domain-config>
    </network-security-config>
複製代碼

更詳細的信息請參考Android Security Config

數字證書轉化https://blog.csdn.net/xiangguiwang/article/details/76400805

相關文章
相關標籤/搜索