關於應用程序根證書

概述:

互聯網上傳輸的任何數據都可以輕而易舉的被髮送者和接收者之外的第三方所獲得。如果敏感的或隱私的數據以明文方式發送,將會造成信息的泄露。例如,網上銀行的各種操作,電子商務交易的數據等是不允許交易的第三方獲悉的。因此我們需要把數據加密後傳輸,通常我們稱傳輸加密數據的鏈接叫做安全鏈接。

安全鏈接和安全的數據傳輸面臨三個難題:1. 如何確認交易雙方不是仿冒的。2. 怎樣對數據進行加密。3. 如何保證傳輸的數據沒有被篡改。證書就是用來在建立鏈接時確認交易雙方身份的一個良好解決辦法。
如果手機中沒有預置證書,或在彈出無證書或證書錯誤的提示時仍然手動鏈接,潛在的危險是終端用戶的隱私信息被被釣魚網站或第三方攻擊者獲取。

可以使用數字證書標識設備,以實現多種目的,包括 VPN 或 Wi-Fi 網絡訪問,以及“電子郵件”或 Chrome 瀏覽器等應用對服務器的身份驗證。

Android 支持以 .crt 或 .cer 擴展名的文件形式保存的 DER 編碼 X.509 證書。如果您的證書文件帶有 .der 或其他擴展名,則必須將其更改爲 .crt 或 .cer,否則無法安裝。
Android 也支持以 .p12 或 .pfx 擴展名的 PKCS#12 密鑰庫文件形式保存的 X.509 證書。如果您的密鑰庫文件帶有其他擴展名,則必須將其更改爲 .p12 或 .pfx,否則無法安裝。通過 PKCS#12 密鑰庫安裝證書時Android 會同時安裝所有隨附的私有密鑰或證書授權中心的證書。

Android根證書管理

設置路徑:
setting=>Security&Location=>Encryption&credentials=>Trusted credentials
手機目錄:
system/etc/security
aosp源碼:
system/ca-certificates

ca

在 Android 系統的 Java 應用程序中,證書驗證通常由不同層面的多個組件完成。第一步的證書合法性驗證,主要由 Java 標準庫的 javax.net.ssl.SSLSocket 在 startHandshake() 方法中完成,後面兩個步驟由更上層的組件完成

其中 cacerts_google 目錄下的根證書,主要用於 system/update_engine、external/libbrillo 和 system/core/crash_reporter 等模塊,cacerts 目錄下的根證書則用於所有的應用。cacerts 目錄下的根證書,即 Android 系統的根證書庫

管理系統根證書庫

他們是 PEM 格式的 X.509 證書。Android 系統通過 SystemCertificateSource、DirectoryCertificateSource 和 CertificateSource 等類管理系統根證書庫。

CertificateSource

frameworks/base/core/java/android/security/net/config/CertificateSource.java

主要是對根證書的獲取和查找:

package android.security.net.config;

import java.security.cert.X509Certificate;
import java.util.Set;

/** @hide */
public interface CertificateSource {
    Set<X509Certificate> getCertificates();
    X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
    X509Certificate findByIssuerAndSignature(X509Certificate cert);
    Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert);
    void handleTrustStorageUpdate();
}

DirectoryCertificateSource 類則基於文件系統上分開存放的根證書文件的形式保存的根證書庫,提供證書的創建、獲取和查找操作

frameworks/base/core/java/android/security/net/config/DirectoryCertificateSource.java

其中獲取根證書庫的 getCertificates() 操作在第一次被調用時,遍歷文件系統,並加載系統所有的根證書文件,並緩存起來,以備後面訪問。根證書的查找操作,主要依據證書文件的文件名進行,證書文件被要求以 [SubjectName 的哈希值].[Index] 的形式命名。

SystemCertificateSource
frameworks/base/core/java/android/security/net/config/SystemCertificateSource.java)了系統根證書庫的路徑,以及無效一個根證書的機制:

public final class SystemCertificateSource extends DirectoryCertificateSource {
    private static class NoPreloadHolder {
        private static final SystemCertificateSource INSTANCE = new SystemCertificateSource();
    }

    private final File mUserRemovedCaDir;

    private SystemCertificateSource() {
        super(new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts"));
        File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        mUserRemovedCaDir = new File(configDir, "cacerts-removed");
    }

    public static SystemCertificateSource getInstance() {
        return NoPreloadHolder.INSTANCE;
    }

    @Override
    protected boolean isCertMarkedAsRemoved(String caFile) {
        return new File(mUserRemovedCaDir, caFile).exists();
    }
}

Android 系統的根證書位於 /system/etc/security/cacerts/ 目錄下。用戶可以通過將特定根證書複製到用戶配置目錄的 cacerts-removed 目錄下來無效一個根證書。

Android framework 還提供了另外一個用於加載並訪問用戶根證書庫的組件 UserCertificateSource,這個類的定義(位於 frameworks/base/core/java/android/security/net/config/UserCertificateSource.java)如下:

public final class UserCertificateSource extends DirectoryCertificateSource {
    private static class NoPreloadHolder {
        private static final UserCertificateSource INSTANCE = new UserCertificateSource();
    }

    private UserCertificateSource() {
        super(new File(
                Environment.getUserConfigDirectory(UserHandle.myUserId()), "cacerts-added"));
    }

    public static UserCertificateSource getInstance() {
        return NoPreloadHolder.INSTANCE;
    }

    @Override
    protected boolean isCertMarkedAsRemoved(String caFile) {
        return false;
    }
}

證書鏈合法性驗證

證書的合法性由 Java 標準庫的 javax.net.ssl.SSLSocket 在 startHandshake() 方法中完成。對於 Android 系統而言,SSLSocket 基於 OpenSSL 庫實現,這一實現由 external/conscrypt 模塊提供,SSLSocket 的實現爲 OpenSSLSocketImpl 類(位於external/conscrypt/src/main/java/org/conscrypt/OpenSSLSocketImpl.java)。

OpenSSLSocketImpl.startHandshake() 中的 SSL/TLS 握手是一個極爲精巧的過程,我們略過詳細的握手過程,主要關注證書驗證的部分。

    /**
     * Returns the sslSessionNativePointer of the negotiated session. If this is
     * a server negotiation, supplying the {@code alpnProtocols} will enable
     * ALPN negotiation.
     */
    public static native long SSL_do_handshake(long sslNativePointer,
                                               FileDescriptor fd,
                                               SSLHandshakeCallbacks shc,
                                               int timeoutMillis,
                                               boolean client_mode,
                                               byte[] npnProtocols,
                                               byte[] alpnProtocols)
        throws SSLException, SocketTimeoutException, CertificateException;

NativeCrypto 類內部定義了一組將會在本地層由與 SSL 握手相關的 OpenSSL C/C++ 代碼調用的回調 SSLHandshakeCallbacks,在上面的 SSL_do_handshake() 方法中,這組回調作爲參數傳入本地層

其中 verifyCertificateChain() 回調用於服務端證書的驗證。Android 系統通過這一回調,將根證書庫的管理模塊和底層 OpenSSL 的 SSL/TLS 握手及身份驗證連接起來。

SSLHandshakeCallbacks 回調由 OpenSSLSocketImpl 實現

私有 CA 簽名證書的應用

自簽名證書是無需別的證書爲其簽名來證明其合法性的證書,根證書都是自簽名證書。私有 CA 簽名證書則是指,爲域名證書籤名的 CA,其合法有效性沒有得到廣泛的認可,該 CA 的根證書沒有被內置到系統中。

在實際的開發過程中,有時爲了節省昂貴的購買證書的費用,而想要自己給自己的服務器的域名簽發域名證書,這即是私有 CA 簽名的證書。爲了能夠使用這種證書,需要在客戶端預埋根證書,並對客戶端證書合法性驗證的過程進行干預,通過我們預埋的根證書爲服務端的證書做合法性驗證,而不依賴系統的根證書庫。

自定義 javax.net.ssl.SSLSocket 的代價太高,通常不會通過自定義 javax.net.ssl.SSLSocket 來修改服務端證書的合法性驗證過程。以此爲基礎,從上面的分析中不難看出,要想定製 OpenSSLSocketImpl 的證書驗證過程,則必然要改變 SSLParametersImpl,要改變 OpenSSLSocketImpl 的 SSLParametersImpl,則必然需要修改 SSLSocketFactory。修改 SSLSocketFactory 常常是一個不錯的方法。

安裝證書

安裝證書,請按以下步驟操作:

1 將證書或密鑰庫從計算機複製到設備的內存設備的根目錄下(也就是說,不要複製到某個文件夾中)。
2 依次轉到設置 > 個人 > 安全 > 憑據存儲 > 從存儲設備安裝。
3 觸摸證書或密鑰庫的文件名即可安裝。系統只顯示您尚未安裝的證書。
4 根據系統提示輸入密鑰庫的密碼,然後觸摸確定。
5 爲該證書輸入一個名稱,然後觸摸確定。

通常,系統會同時安裝客戶端證書隨附的 CA 證書。您也可以通過相同步驟安裝單獨的 CA 證書。
對於“電子郵件”和“瀏覽器”等支持證書的應用,可直接從應用內部安裝證書

使用 CA 證書

依次觸摸設置 > 個人 > 安全 > 憑據存儲 > 受信任的憑據。“受信任的憑據”屏幕有以下兩個標籤:
系統顯示永久安裝在手機 ROM 中的證書授權中心 (CA) 的證書。
用戶顯示您自行安裝的所有 CA 證書,例如安裝客戶端證書過程中安裝的 CA 證書。
要查看 CA 證書的詳情,請觸摸其名稱。相應詳情會顯示在滾動屏幕中。

QS

爲什麼手機設置->安全設置->證書管理中有可以查看證書,而瀏覽器->設置->安全設置->信任證書中也可以查看證書?這他們查看到的證書有什麼區別?分別有什麼作用?

手機證書管理器:
不支持WTLS格式的證書。
最多支持30個證書,如果要預置多餘30個證書,需要打patch解決。
支持通過文件系統安全證書。方法:打開文件管理器,選中需要安裝的證書。點擊左鍵,選擇安裝。
存儲的證書有兩大類用途:驗證網絡上服務器的身份;驗證java midlet的來源是否合法。
用於驗證網絡上服務器身份的證書與瀏覽器證書管理器中預置的證書是相同的。用於驗證java應用來源的證書是通過數組custpack_java_root_ca來預置的。
用於安全鏈接的證書僅被OPEN SSL、CIC LIB這兩個SSL庫用到。
瀏覽器證書管理器:
支持X.509證書和WTLS證書。
所支持的證書個數沒有限制。
不支持終端用戶自行安裝證書,但支持通過網絡下載安裝證書。所下載的文件的content type必須爲application/vnd.wap.hashed-certificate, application/vnd.wap.signed-certificate。
使用瀏覽器訪問安全網站或通過WPS上網時都會用到這個管理器中的證書。

怎樣去掉訪問安全網站時彈出的pop框?
方案一 :往手機中添加相應證書
如果往手機中添加的證書能夠驗證server傳過來的證書,在登錄這個網站時手機就能自動認證這個網站而不會有提示對話框。這需要由貴司找對應的網站或者提供證書文件的第三方公司申請對應的證書文件。將證書通過代碼添加到手機中。由於全世界的安全網站很多,手機中不可能能認證所有安全網站。所以添加證書只能保證訪問某些網站不出現證書錯誤,在訪問另外一些安全網站還是可能出現這個提示對話框給用戶。
該方案僅能保證去除”No trusted certificate, continue?”的提示。對於其他提示不起作用。
方案二:在證書驗證階段忽略掉這些提示
因爲出現這個對話框不會影響用戶訪問,手機端默認用戶需要訪問的安全網站是用戶信任的安全網站,因此去掉這個提示對話框。去掉對話框也不會影響手機通過安全連接方式與server交互數據。如果選擇這個解決方案,需要由我們提供patch給貴司,在拿到這個patch後貴司就可以通過宏:BRA_CFG_SEC_CONN_WITHOUT_CERT_DLG 來選擇需要這兩個對話框還是去掉這兩個對話框(默認爲0表示去除,1表示保留)。但去掉對話框的話可能不符合國外某些運營商的規範(在訪問不受信任的站點時提示用戶)。貴司如果有這個patch的話也可以自行打開這兩個對話框。
目前這個patch可以去掉"No trusted certificate, continue?”, "Error in certificate. continue?”以及"Unknown server, continue?”的提示。
對於"Remove existing old trust certificate?”的提示,不能通過patch的方式去除。貴司可以通過internet service->settings->security settings->trusted certificates查看證書及其過期時間,找到過期證書後從代碼中將其移除。
目前已確認過期的證書是verisign_rsa_ca,請在custpack_root_ca這個數組中刪除
{verisign_rsa_ca, sizeof(verisign_rsa_ca), CERT_FORMAT_X509, CERT_DOMAIN_OPERATOR, CERT_READ_ONLY_ON}。
其他我司release code時預置的證書最早在2018年纔會過期。
對於09BW10.12及其之後的版本,可在函數mmi_brw_sec_confirm_dialog_ind中的code body開始時加上如下代碼:
/don’t display certificate related popups. if you need remove other popup about certificate, please add string id as a case/
switch (localBuff_p->title_id)
{
case SEC_STR_ID_CERT16: //no certificate
case SEC_STR_ID_CERT19: //error in certificate
case SEC_STR_ID_CERT18: //unknown server
{
wps_show_confirm_dialog_res_struct myMsgPtr;
myMsgPtr = (wps_show_confirm_dialog_res_struct
) OslConstructDataPtr(sizeof(wps_show_confirm_dialog_res_struct));
myMsgPtr->request_id = localBuff_p->request_id;
myMsgPtr->result = WPS_DLG_OK;
mmi_brw_event_hdlr_send_ilm_to_wps(MSG_ID_WPS_SEC_SHOW_CONFIRM_DIALOG_RES, myMsgPtr, NULL);
return;
}
default:
break;
}

方案三:修改提示對話框的內容
因爲提示No和error信息終端用戶可能會認爲手機發生錯誤而影響正常訪問,通常會誤認爲是手機沒有授權訪問這個網站。因此貴司可以考慮參考對比機修改提示給用戶提示的內容(修改字符串資源SEC_STR_ID_CERT7,SEC_STR_ID_CERT16,SEC_STR_ID_CERT18,SEC_STR_ID_CERT19),例如Nokia N72在遇到server傳回的證書無法驗證的情況下會提示用戶:“是否確認繼續瀏覽這個安全網站?”,在訪問該手機能認證的安全網站時不會給用戶提示上述信息

如何往手機中添加證書

mtk平臺只支持WTLS (16進制數據以0X01開頭) 或者 X.509(16進制數據以0X30開頭)格式並且是DER 編碼方式的證書

拿到證書文件後把它的證書轉成十六進制的數據(可以通過16進制編輯器如Ultraedit完成),可以參考custpack_certs.c裏已有證書添加16進制數據,參考數組custpack_root_ca將證書加載進這個數組。同時更新custpack_certs.h裏NUMBER_OF_CERTIFICATES爲現有證書個數,如果增加較多證書就需要修改最多證書個數CUSTPACK_CFG_MAX_CERTS_NUM(默認30),詳細信息可以參考DMS上文檔:How to preinstall CA.ppt,08A及較早的工程證書數據寫在數組wap_root_ca_default中。

需要注意的是在添加某網站對應的證書後download新的bin文件後必須format FAT才能通過菜單Wap->Settings->Trusted certificates查看已經添加的證書。另外請注意要驗證添加的證書是否有效必須將手機設置爲正確的時間。

如果手頭上的證書不是DER編碼的,則請先在電腦上打開此證書,然後在對話框的"詳細信息"->"複製到文件"嚮導中將證書保存爲DER編碼。

參考文章:
Android 根證書管理與證書驗證

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章