簡體   English   中英

Android SSL 證書固定

[英]Android SSL Certificate pinning

我知道有很多關於在 Android 中固定證書的問題,但我找不到我要找的東西......

checkServerTrusted() SSLSocketFactory並覆蓋了checkServerTrusted()方法。 在這種方法中,我執行以下操作:

CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate ca = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(PUB_KEY.getBytes("UTF-8")));
for (X509Certificate cert : chain) {
    // Verifing by public key
    cert.verify(ca.getPublicKey());                      
}

鏈中的一項進行驗證,而另一項不進行驗證(這會引發Exception )。 我想我無法掌握證書鏈的工作原理。

同一個公共證書是否應該與鏈中的所有證書一起驗證?

我發現在 Android 上實現證書固定的最簡單方法是使用OkHttp庫。

以下是文檔摘錄

默認情況下,OkHttp 信任宿主平台的證書頒發機構。 此策略可最大限度地提高連接性,但會受到證書頒發機構攻擊,例如2011 DigiNotar 攻擊 它還假定您的 HTTPS 服務器的證書是由證書頒發機構簽署的。

使用CertificatePinner來限制受信任的證書頒發機構。 證書鎖定可提高安全性,但會限制您的服務器團隊更新其 TLS 證書的能力。 未經服務器 TLS 管理員的許可,請勿使用證書鎖定!

  public CertificatePinning() {
    client = new OkHttpClient();
    client.setCertificatePinner(
        new CertificatePinner.Builder()
            .add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
            .add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
            .add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
            .add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
            .build());
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/robots.txt")
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    for (Certificate certificate : response.handshake().peerCertificates()) {
      System.out.println(CertificatePinner.pin(certificate));
    }
  }

如果您需要支持自簽名證書,那么OkHttp 支持接受自簽名 SSL 證書嗎? 會指導你。

同一個公共證書是否應該與鏈中的所有證書一起驗證?

答案:- 沒有。

大多數公共 CA 不直接簽署服務器證書。 相反,他們使用他們的主 CA 證書(稱為root CA )來簽署中間 CA。 他們這樣做是為了讓根 CA 可以離線存儲,以降低被入侵的風險。 但是,像 Android 這樣的操作系統通常trust only root CAs直接trust only root CAs ,這在服務器證書(由中間 CA 簽名)與知道根 CA 的證書驗證者之間留下了短暫的信任差距。

為了解決這個問題,服務器在 SSL 握手期間不會只向客戶端發送它的證書,而是從服務器 CA 通過到達受信任的根 CA 所需的任何中間件的證書鏈。

查看此鏈接以獲取更多信息。 希望這會對用戶有所幫助。

證書和公鑰固定(又名證書固定)簡而言之 -

通常,應用程序信任所有預安裝的 CA。 如果這些 CA 中的任何一個要頒發欺詐性證書,則該應用程序將面臨中間人攻擊(又名竊聽)的風險。 一些應用程序選擇通過限制他們信任的 CA 集或通過證書鎖定來限制他們接受的證書集。 證書固定是通過公鑰散列提供一組證書來完成的。 證書固定是一種依賴於客戶端服務器證書驗證的方法。

以下是在 Android 上實現證書鎖定的 3 種方法 -


特別是對於您的問題,您可以使用<pin-set>標簽通過 NSC 中公鑰的散列來配置證書。

請注意,在使用證書固定時,您應該始終包含一個備份密鑰,以便如果您被迫切換到新密鑰或更改 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>

您的問題和疑慮

同一個公共證書是否應該與鏈中的所有證書一起驗證?

不,因為鏈中的每個證書(根證書、中間證書和葉證書)都使用不同的私鑰/公鑰對進行簽名。

鏈中的一項驗證,另一個不驗證(這會引發異常)。 我想我無法掌握證書鏈的工作原理。

那是因為您的證書是葉子證書,因此您只能根據它來驗證您的公鑰,而不能針對根證書和中間證書進行驗證。

代碼方法

我繼承了 SSLSocketFactory 並覆蓋了 checkServerTrusted() 方法。

如果您真的想自己編寫代碼,我建議您改用內置的 OkHttp Ceritficate Pinner,您可以像這樣構建:

import okhttp3.CertificatePinner;

public class OkHttpPinnerService {

    // true if the Approov SDK initialized okay
    private boolean initialized;

    // cached OkHttpClient to use or null if not set
    private OkHttpClient okHttpClient;

    public synchronized OkHttpClient getOkHttpClient() {
        if (okHttpClient == null) {
            // build a new OkHttpClient on demand
            if (initialized) {
                // build the pinning configuration
                CertificatePinner.Builder pinBuilder = new CertificatePinner.Builder();
                Map<String, List<String>> pins = YourConfig.getPins("public-key-sha256");
                for (Map.Entry<String, List<String>> entry : pins.entrySet()) {
                    for (String pin : entry.getValue())
                        pinBuilder = pinBuilder.add(entry.getKey(), "sha256/" + pin);
                }

                // build the OkHttpClient with the correct pins preset and ApproovTokenInterceptor
                Log.i(TAG, "Building new Approov OkHttpClient");
                okHttpClient = okHttpBuilder.certificatePinner(pinBuilder.build()).build();
            } else {
                // if the Approov SDK could not be initialized then we can't pin or add Approov tokens
                Log.e(TAG, "Cannot build Approov OkHttpClient due to initialization failure");
                okHttpClient = okHttpBuilder.build();
            }
        }

        return okHttpClient;
    }    
}

代碼沒有經過語法錯誤或邏輯正確性測試。 我只是從這個 repo 中復制了它並稍微修改了它。

無代碼方法

由於 Android API 24 可以通過內置的安全配置文件將證書固定到公鑰哈希,這不需要編寫任何代碼,只需將正確配置的network_security_config.xml文件添加到您的項目中即可。

為避免在構建network_security_config.xml文件時出錯,我建議您使用移動證書鎖定生成器來提取要鎖定的域所使用的實時 pin,並為您構建正確的配置。 例如:

從將域添加到固定

安卓網絡安全配置

現在只需將生成的配置復制粘貼到項目中的network_security_config.xml文件中,並將該文件添加到AndroifManifest.xml 只需按照頁面上的說明操作即可。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM