Android Keystoreの概要

昨今、システム開発においてセキュリティの重要性は増しています。
Androidのセキュリティ機構としては、Android Keystoreシステムなどが挙げられますね。
この[Android Keystore](https://developer.android.com/training/articles/keystore?hl=en)はAndroid developersのサイトに説明があるのですが、日本語だとちょっと読みづらい所があったので、自分なりに概要をまとめてみました。
# Android Keystoreシステムとは
ざっくりまとめると、**「Androidデバイス内の安全な領域に暗号化キーを保管するための仕組み**」と言えそうです。
暗号化キーをアプリ内にそのまま保持したりすると、ルート化された端末だと丸見えになります。
「セキュアなデータを端末内に保持する必要がある」などのセキュリティが重視される要件ではAndroid Keystoreの使用も検討しましょう。

## Android Keystoreの特徴
1. 暗号化キーはデバイスから抽出が困難なコンテナ内に保管される
2. 特定の暗号化モードでしか暗号化キーを使用できないように設定することができる
3. 暗号化キーを使用する際にユーザー認証を求めることができる

### Android Keystoreはどうやって暗号化キーのキーマテリアル抽出を困難にしているのか
- キーマテリアル(注1)の使用を「暗号化操作を実行するシステムプロセス」にしか許可しない
- 「アプリプロセス」にキーマテリアルの使用を許可しない
-  Androidデバイスの安全なハードウェア（Trusted Execution Environment（TEE）、セキュア エレメント（SE）など）にキーマテリアルをバインドできるようにしている

注1… 解釈が曖昧ですが、ここでは「暗号化キー生成の元となるデータ」を指していると思われます。

### 暗号化キーの作成方法
以下のソースコードは、AndroidKeystoreプロバイダを利用して暗号化キーを作成する手順の一例です。
ここではRSAアルゴリズムの鍵ペアを作成しています。
共通鍵等のその他の鍵の生成方法や、暗号モードの指定方法の詳細は[Android developers](https://developer.android.com/training/articles/keystore?hl=en)などを参考にしましょう。
（例外処理は省略）

#### Android Keystoreインスタンスの取得
```kotlin:AndroidKeyStoreインスタンスの取得
    /*
    Keystore.getInstanceの引数に"AndroidKeyStore"を指定し、Android KeyStoreのインスタンスを取得する。
    このインスタンスはフィールドにAndroidKeyStoreSpiのインスタンスを保持する。
     */
    val keyStore: KeyStore = KeyStore.getInstance("AndroidKeyStore")
    //Android KeyStoreをロードする。内部のAndroidKeystoreSpiがこのメソッドにより初期化されるため、この処理が必要
    keyStore.load(null)
```

#### 鍵ペアの生成
```kotlin:鍵ペアの生成
    /*
    鍵ペアを作成するためのKeyPairGeneratorのインスタンスを取得する
    引数のproviderに"AndroidKeyStore"を指定することで、
    Android Keystoreに鍵ペアを作成するKeyPairGeneratorSpiインスタンスを取得する
     */
    val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_RSA,
        "AndroidKeyStore"
    )
    
    // 作成する鍵ペアのスペックを指定するためのKeyGenParameterSpecインスタンスを生成
    val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
        "hoge", //エイリアスを"hoge"に設定。Android KeyStoreからエントリーを取得する際はこのエイリアスを使用する。
        KeyProperties.PURPOSE_SIGN//鍵の使用目的を指定する。ここでは署名のみを目的とした鍵を生成。指定した目的以外で鍵を使用するとInvalidKeyExceptionが発生する
    ).run {
        //使用するダイジェストのアルゴリズムをSHA-256に限定する。これ以外のダイジェストアルゴリズムの使用は拒否される。
        setDigests(KeyProperties.DIGEST_SHA256)
        //使用するパディングモードの指定。RSA鍵ペアを生成する場合は指定必須。指定したパディングモード以外は拒否される。
        setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
        build()
    }

    //指定したKeyGenParameterSpecでKeyPairGeneratorを初期化
    kpg.initialize(parameterSpec)
    //KeyPairGeneratorで鍵ペアを生成
    val kp = kpg.generateKeyPair()
```

#### データへの署名処理
```kotlin:データへの署名
    //署名対象のデータ
    val str = "sign"
    val data = str.toByteArray(StandardCharsets.UTF_8)

    // Android KeyStoreのインスタンスを取得
    val keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }

    //Android KeyStoreからエイリアスを指定してKeystoreエントリを取得する
    val entry = keyStore.getEntry("hoge", null)

    //署名を作成/検証するためのSignatureインスタンスを取得。引数は適宜変更必要。
    val s = Signature.getInstance("SHA256withRSA")

    //秘密鍵を指定してSignatureインスタンスを初期化する。ここでは変数entryはprivate keyであること前提
    s.initSign((entry as KeyStore.PrivateKeyEntry).privateKey)

    //Signatureインスタンスによる署名対象データの更新を実行
    s.update(data)
    //署名対象データに署名する
    val signature = s.sign()
    //署名したデータをBase64エンコード
    val result = Base64.encodeToString(signature, Base64.DEFAULT)
```

Android KeyStoreを利用した暗号化キー生成を実装してみましたが、触ってみると非常に奥が深いですね。TEEやSEなどの詳細も気になるところですが、今回はここまでです。