昨今、システム開発においてセキュリティの重要性は増しています。

Androidのセキュリティ機構としては、Android Keystoreシステムなどが挙げられますね。

このAndroid Keystoreは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などを参考にしましょう。

(例外処理は省略)


Android Keystoreインスタンスの取得


AndroidKeyStoreインスタンスの取得

    /*

Keystore.getInstanceの引数に"AndroidKeyStore"を指定し、Android KeyStoreのインスタンスを取得する。
このインスタンスはフィールドにAndroidKeyStoreSpiのインスタンスを保持する。
*/

val keyStore: KeyStore = KeyStore.getInstance("AndroidKeyStore")
//Android KeyStoreをロードする。内部のAndroidKeystoreSpiがこのメソッドにより初期化されるため、この処理が必要
keyStore.load(null)


鍵ペアの生成


鍵ペアの生成

    /*

鍵ペアを作成するための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()



データへの署名処理


データへの署名

    //署名対象のデータ

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などの詳細も気になるところですが、今回はここまでです。