[翻譯]使用 ICS KeyChain API(Using the ICS KeyChain API)

weixin_34054866發表於2018-10-02

前言:

這是一篇關於Android安全的翻譯,這是原文地址。這篇文章主要講了通過 KeyChain 安裝和使用私有證書的方法。證書相關處理在 Android 應用開發中並不常見,但是在企業應用中是不可或缺的。

我會一段一段地複製原文,然後在下面給出翻譯,如有圖片,我會重新儲存再上傳,以免出現“圖片無法顯示的情況”,因為原文不能直接訪問。


原文:

Using the ICS KeyChain API

November 29, 2011

譯文:

使用 ICS (冰激凌三明治安卓4.0系統代號) KeyChain 應用程式設計介面

2011年11月29日

原文:

Update: Sample app code is now available on github.

譯文:

更新:示例應用程式碼現在在 github 上

原文:

The recently released Android 4.0 (Ice Cream Sandwich, ICS) introduces a new, unified UI for both tablets and handsets, lots of 'people-centric' communication and sharing features and other convenient improvements such as a better camera app and the much-hyped face unlock. Since everyone is talking about those, we will have a look at some of the less-user visible, but nonetheless important security-related improvements.

譯文:

最近釋出的 Android 4.0 (冰激凌三明治, ICS) 介紹了一個全新的,統一平板和手機的 UI 。很多'以人為中心'的互動和共享特性,以及其它方便的改進,例如更好的相機應用,過度炒作的人臉解鎖。由於每個人都在討論這些,我們要看看那些使用者很少看見,但卻是重要的安全相關的改進。

原文:

Android is often said to be missing crucial security features to be seriously accepted in the corporate world, which has long been the domain of RIM's BlackBerry. Two of those missing features were the ability to control the system's trusted CA certificates and offer a centralized secure credential storage. Since many companies use private PKI's, the ability to install trusted certificates system-wide is essential for using corporate services secured by those PKI's. Until now, the only way to use those was to embed the needed CA certificates in each application and create custom TrustStores to be able to connect using SSL. A system-wide credential storage has actually been available for a while, but it was only usable by the built-in VPN and WiFi (EAP) clients. One could install a private key/certificate pair using the Settings app, but there was no public API to access the installed keys from applications. ICS offers SDK API's for both trusted certificate management and the secure credential storage via the KeyChain class. We will have a look at how it is used in the following sections.

譯文:

Android 經常被說成缺失了重要的安全特性,並且被企業界嚴肅地接受了,長久以來這是 RIM (譯註:加拿大RIM公司)黑莓的領域。兩個缺失的特性是:控制系統的受信任 CA 證書的能力,提供一箇中心化的證書儲存。由於很多公司使用私有的 PKI(Public Key Infrastructure 公鑰基礎設施),在系統層面安裝信任證書對於使用受 PKI 保護的企業服務是必不可少的。直到現在,使用這些所需 CA 證書的唯一途徑是嵌入到每個應用中,並且建立自定義的 TrustStores,用 SSL 連線。一個系統層面可用的證書儲存已經存在一段時間了,但它只對內建的 VPN 和 WiFi (EAP) 客戶端可用。人們可以通過“設定”應用安裝私有的憑據/證書對 (key/certificate) 。但是沒有通過應用實現安裝的公開 API 。通過 KeyChain 類,ICS 給信任證書管理和安全證書儲存提供了 SDK API。我們在下面的部分要看看它如何使用。

原文:

The KeyChain class is deceptively simple: it offers only 4 public static methods, but those are sufficient to do most certificate-related tasks. Let's first see how one would install a private key/certificate pair and use those to sign and verify some data. The KeyChain API lets you install a private key/certificate pair bundled in a PKCS#12 file. Instead of offering an API to directly install the key and certificate, KeyChain provides a factory method, createInstallIntent() that returns a system intent to parse and install keys/certificates (that is actually the same intent offered by the Settings app in previous versions). To install a PKCS#12 file, you have to read it to a binary array, store it under the EXTRA_PKCS12 key in the intent's extras, and start the associated activity:

譯文:

KeyChain 類看似簡單,它提供了4個公開的靜態方法,但這些方法對大多數證書相關工作來說足夠了,首先讓我們看看如何安裝一個私有的憑據/證書對,並用它簽名和驗證一些資料。KeyChain API 讓你安裝一個包裝在 PKCS#12 檔案中的私有的 key/certificate 對。而不是提供一個 API 來直接安裝私鑰和證書,KeyChain 提供了一個工廠方法 createInstallIntent(),它返回一個系統 intent 來解析和安裝 keys/certificates , (它實際上和以前版本中設定應用提供的 intent 是相同的)。要安裝一個 PKCS#12 檔案,你必須把它讀進一個二進位制陣列,把它儲存在 intent extrasEXTRA_PKCS12 欄位中,然後啟動相關的 activity

Intent intent = KeyChain.createInstallIntent();
byte[] p12 = readFile("keystore-test.pfx");
intent.putExtra(KeyChain.EXTRA_PKCS12, p12);
startActivity(intent);

原文:

This will prompt you for the PKCS#12 password in order to extract and parse the key and certificate. If the password is correct, you will be prompted for a 'certificate name' as shown in the screenshot below. If the PKCS#12 has a friendly name attribute it will be shown as the default, if not you will just get a long hexadecimal hash string. The string you enter here is the key/certificate alias you will use to access those later via the KeyChain API. You will be prompted to set a lock screen PIN or password to protect the credential storage if you haven't already set one.

譯文:

為了解壓並解析憑據和證書,這會提示你輸入 PKCS#12 的密碼,如果密碼正確,會提示輸入‘證書名稱’,像下面的截圖展示的一樣。如果 PKCS#12 有一個友好的名稱屬性,它預設會被顯示,如果沒有,你會得到一個很長的十六進位制 hash 串。你在這兒輸入的字串是key/certificate的別名,在後面你要用它們通過 KeyChain API 訪問 key/certificate。如果你還沒有設定過,會提示你設定一個鎖屏 PIN (Personal Identification Number,個人識別密碼),或者密碼來保護證書儲存,

11623847-f39109a2902cd19d.png
pkcs12-install.png

原文:

To use a private key stored in the system credential storage, you need to call KeyChain.choosePrivateKeyAlias() and provide a callback implementation that receives the selected alias:

譯文:

要用系統證書儲存中的一個私有的金鑰庫,你需要呼叫 KeyChain.choosePrivateKeyAlias() 並且提供一個回撥實現,它接收已選的別名:

public class KeystoreTest extends Activity implements OnClickListener,
     KeyChainAliasCallback {

    @Override
    public void onClick(View v) {
        KeyChain.choosePrivateKeyAlias(this, this, 
           new String[] { "RSA" }, null, null, -1, null);
    }

    @Override
    public void alias(final String alias) {
        Log.d(TAG, "Thread: " + Thread.currentThread().getName());
        Log.d(TAG, "selected alias: " + alias);
    }
}

原文:

The first parameter is the current context, the second -- the callback to invoke, and the third and forth specify the acceptable keys (RSA, DSA or null for any) and acceptable certificate issuers for the certificate matching the private key (Edit: it turns out both keyTypes and issuers are currently unused, so just pass null). The next two parameters are the host and port number of the server requesting a certificate, and the last one is the alias to preselect. We leave all but the key type as unspecified (null or -1) here to be able to select from all available certificates. One thing to note here is that the alias() callback will not be called on the main thread, so you shouldn't try to directly manipulate the UI (it is called on a binder thread). Using the key requires user authorization, so Android will display a key selection dialog which also serves to allow access to the selected key.

譯文:

第一個引數是當前的 context, 第二個是回撥,第三第四是可接受的金鑰型別(RSA, DSA,或者 null 表示所有)和可接受的證書發行人,為了讓證書匹配私鑰。(修訂:發現 keyTypesissuers 當前都沒有用,所以只傳了 null)接下來的兩個引數是請求證書的伺服器主機和埠號,最後一個是預選的別名。除了金鑰型別,這裡其它的都不去指定(null 或者 -1),這樣可以選擇所有可用的證書。需要注意的是 alias() 回撥不會在主執行緒呼叫,因此你不應該嘗試直接操作 UI (它在一個 binder 執行緒呼叫)。使用金鑰需要使用者授權,因此 Android 會顯示一個私鑰選擇對話方塊,這個對話方塊還用來同意訪問已選的私鑰。

11623847-525075c77780831e.png
cert-select.png

原文:

In order to get a reference to a private key, you need to call the KeyChain.getPrivateKey() method passing the key alias name received in the previous step. This doesn't seem to be documented but if you try to call this method on the main thread you will get an exception saying that this may 'lead to a deadlock'. Here we call it on a background thread using AsyncTask (which is almost always the right thing to do when dealing with potentially time-consuming I/O operations).

譯文:

為了得到一個私鑰的引用,你需要呼叫 KeyChain.getPrivateKey() 方法,傳遞前一步接收到的私鑰別名。看起來沒有文件說明,但當你嘗試在主執行緒呼叫這個方法,你會得到一個異常,說這可能會 ‘導致死鎖’。這裡我們在一個後臺執行緒呼叫它,用 AsyncTask(當處理潛在的耗時 I/O 操作,這幾乎總是正確的)。

new AsyncTask<Void, Void, Boolean>() {

    private Exception error;

    @Override
    protected Boolean doInBackground(Void... arg) {
        try {
            PrivateKey pk = KeyChain.getPrivateKey(ctx,
                    alias);
            X509Certificate[] chain = KeyChain.getCertificateChain(ctx, 
                    alias);
       
            byte[] data = "foobar".getBytes("ASCII");
            Signature sig = Signature.getInstance("SHA1withRSA");
            sig.initSign(pk);
            sig.update(data);
            byte[] signed = sig.sign();

            PublicKey pubk = chain[0].getPublicKey();
            sig.initVerify(pubk);
            sig.update(data);
            boolean valid = sig.verify(signed);
            Log.d(TAG, "signature is valid: " + valid);

            return valid;
       } catch (Exception e) {
           e.printStackTrace();
           error = e;

           return null;
       }
   }

   @Override
   protected void onPostExecute(Boolean valid) {
        if (error != null) {
            Toast.makeText(ctx, "Error: " + error.getMessage(),
                    Toast.LENGTH_LONG).show();

            return;
        }

        Toast.makeText(ctx, "Signature is valid: " + valid,
            Toast.LENGTH_SHORT).show();
    }
}.execute();

原文:

We first get the private key and certificate chain using the key alias and then create and verify a signature to check if the key is actually usable. Since we are using a self-signed certificate the 'chain' consists of a single entry, but for a certificate signed by a CA you will need to find the actual end entity certificate in the returned array.

譯文:

首先我們利用金鑰別名得到私鑰和證書鏈(certificate chain),然後建立並且驗證一個簽名(signature),來檢查金鑰是否真正可用。由於我們使用一個自定義的證書,所以 chain 由一個單獨的實體組成。但對於一個 CA 簽署的證書,你需要在返回的陣列中查詢到真正的證書實體。

原文:

Installing a CA certificate is not very different from installing a PKCS#12 file: you load the certificate in a byte array and pass it as an extra to the install intent.

譯文:

安裝一個 CA 證書和安裝一個 PKCS#12 檔案差別不大,把證書載入到一個位元組陣列,作為一個 extra 傳遞到安裝的 intent

Intent intent = KeyChain.createInstallIntent();
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, cert);
startActivity(intent);

原文:

Android will parse the certificate, and if it's Basic Constraints extension is set to CA:TRUE it will consider it a CA certificate and import it into the user trust store. You will need to authenticate to import the certificate, but the funny thing is that the import dialog does not show neither the certificate DN, nor its hash value. The user has no way of knowing what they are importing, until it's done. Very few people will bother to actually check, so this could be a potential security threat: malicious applications might trick people into installing rogue certificates. Here's how the import dialog looks:

譯文:

Android 會解析證書,如果它的基本約束擴充套件(Basic Constraints extension)被設定成 CA:TRUE,它會被當成是一個 CA 證書,並匯入到使用者信任儲存。你需要授權匯入證書,但有趣的是,匯入對話方塊既不顯示證書 DN (Distinct Name 獨特的名稱),也不顯示它的雜湊值。使用者沒有辦法知道他們在匯入什麼,直到匯入完成。很少有人會真正地費心檢查,因此這會是一個潛在的安全威脅:惡意的應用可能會欺騙使用者安裝流氓證書,匯入對話方塊看起來是這樣的:

11623847-0157ffb5adeadd67.png
ca-cert-install.png

原文:

After the certificate is imported, it will show up in the 'Trusted credentials' screen's 'User' tab (Settings->Security->Trusted credentials). Tapping the certificate entry displays a details dialog, where you can (finally!) check the subject, issuer, validity period, serial number and SHA-1/SHA-256 fingerprints. You can also remove the certificate by pressing the 'Remove' button (scroll down to display it).

譯文:

證書匯入以後,它會在 Trusted credentials 頁面的 User tab 中顯示(設定->安全->信任的證書)。點選證書條目,顯示一個詳情對話方塊,這裡你能(終於!)檢查主題、發行者、有效期限、序列號和 SHA-1/SHA-256 指紋。你也可以通過點選 'Remove' 按鈕刪除證書(下滑顯示)。

11623847-bd3009594bd29a46.png
cert-details.png

原文:

While you can delete individual CA certificates, there is no way to delete individual keys and user certificates. You can delete all by using the 'Clear credentials' option in the Credential storage section of the security settings. Another thing to note is that, as long as you have keys in the credential storage, you cannot remove the screen lock, since it is used to protect access to the keystore. In previous Android versions, there was a separate 'credential storage password', but it seems in ICS they decided to simplify things by using the screen lock password to protect credential storage as well.

譯文:

雖然你能刪除個人的 CA 證書,但沒有辦法刪除個人的金鑰和使用者證書。你可以使用安全-設定-證書儲存區域的 '清除證書' 選項刪除所有的證書。另一個要注意的事是一旦你有私鑰在證書儲存中,你就不能刪除螢幕鎖,由於它被用來保護 keystore 的訪問。在之前的 Android 版本,有一個單獨的'證書儲存密碼'('credential storage password'),但好像在 ICS 他們決定簡化,用鎖屏密碼保護證書儲存。

原文:

The newly introduced KeyChain API lets you install and access private keys in a centralized and secure credential storage, as well as add system-wide trusted certificates. It doesn't provide low-level access to the underlying keystore, utilizing the Android intent dispatching mechanism instead to call a system activity that does the actual work. The CA certificate install dialog is missing a crucial feature (displaying details about the certificate), but all in all, providing the access to the system keystore service is a step in the right direction.

譯文:

新引入的 KeyChain API 讓你在一箇中心化的安全證書儲存中安裝和訪問私鑰,同樣新增系統層面的受信任證書。它沒有提供 keystore 之下的下層訪問,利用 Android intent 的分發機制代替呼叫做實際工作的系統 Activity。CA 證書安裝對話方塊缺少了一個關鍵特性(顯示證書詳情),但總得來說,提供系統 keystore 服務的訪問是朝著正確方向的一個進步。

原文:

That wraps the first part of our Android keystore introduction. In the next part we will look into all that is hidden behind the KeyChain facade, and try to give some details about the underlying implementation.

譯文:

這覆蓋了我們的 Android keystore 介紹的第一部分。在下一部分我們要調查 KeyChain 後面的一切,並且嘗試給出一些關於底層的實現。

相關文章