昨今、システム開発においてセキュリティの重要性は増しています。
Androidのセキュリティ機構としては、Android Keystoreシステムなどが挙げられますね。
このAndroid KeystoreはAndroid developersのサイトに説明があるのですが、日本語だとちょっと読みづらい所があったので、自分なりに概要をまとめてみました。
Android Keystoreシステムとは
ざっくりまとめると、「Androidデバイス内の安全な領域に暗号化キーを保管するための仕組み」と言えそうです。
暗号化キーをアプリ内にそのまま保持したりすると、ルート化された端末だと丸見えになります。
「セキュアなデータを端末内に保持する必要がある」などのセキュリティが重視される要件ではAndroid Keystoreの使用も検討しましょう。
Android Keystoreの特徴
- 暗号化キーはデバイスから抽出が困難なコンテナ内に保管される
- 特定の暗号化モードでしか暗号化キーを使用できないように設定することができる
- 暗号化キーを使用する際にユーザー認証を求めることができる
Android Keystoreはどうやって暗号化キーのキーマテリアル抽出を困難にしているのか
- キーマテリアル(注1)の使用を「暗号化操作を実行するシステムプロセス」にしか許可しない
- 「アプリプロセス」にキーマテリアルの使用を許可しない
- Androidデバイスの安全なハードウェア(Trusted Execution Environment(TEE)、セキュア エレメント(SE)など)にキーマテリアルをバインドできるようにしている
注1… 解釈が曖昧ですが、ここでは「暗号化キー生成の元となるデータ」を指していると思われます。
暗号化キーの作成方法
以下のソースコードは、AndroidKeystoreプロバイダを利用して暗号化キーを作成する手順の一例です。
ここではRSAアルゴリズムの鍵ペアを作成しています。
共通鍵等のその他の鍵の生成方法や、暗号モードの指定方法の詳細はAndroid developersなどを参考にしましょう。
(例外処理は省略)
Android Keystoreインスタンスの取得
/*
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などの詳細も気になるところですが、今回はここまでです。