Android Keystore システムを使用すると、暗号鍵をコンテナ内に格納することで、デバイスからキーを抽出するのを難しくすることができます。キーストアに格納したキーは、エクスポート不可のキーマテリアルを使った暗号化処理に使用できます。さらに、キーを使用するためにユーザー認証を求めたり、特定の暗号化モードでのみキーを使用できるように制限したりするなど、キーをいつどのように使用するかを制限する機能が提供されています。詳細については、セキュリティ機能をご覧ください。
Keystore システムは、Android 4.0(API レベル 14)で導入された KeyChain
API や、Android 4.3(API レベル 18)で導入された Android Keystore プロバイダ機能、Jetpack の一部として利用可能なセキュリティ ライブラリで使用されます。このドキュメントでは、Android Keystore プロバイダをいつどのように使用するのかについて説明します。
セキュリティ機能
Android Keystore システムは、キーマテリアルの不正使用を防止します。最初に、Android Keystore はアプリプロセスおよび Android デバイスからのキーマテリアルの抽出を全体的に防ぐことにより、Android デバイス外部でのキーマテリアルの不正使用を低減します。次に、承認するキーの使用法をアプリで指定し、これらの制限をアプリのプロセスの外部に適用することにより、Android デバイスでのキーマテリアルの不正使用を低減します。
抽出の防止
Android Keystore キーのキーマテリアルは、次の 2 つのセキュリティ対策によって抽出されないようになっています。
- キーマテリアルがアプリプロセスに入ることは決してありません。アプリが Android Keystore キーを使用して暗号処理を実行する際、バックグラウンドでは、署名や検証の対象となるプレーン テキスト、暗号テキスト、メッセージが、暗号処理を実行するシステム プロセスに送られます。アプリプロセスに不正侵入された場合、攻撃者がアプリのキーを使用できるようになる可能性はありますが、キーマテリアルを抽出することは決してできません(キーマテリアルを抽出する目的としては、Android デバイスの外部で使用するなどの用途が考えられます)。
- キーマテリアルは、Android デバイスのセキュア ハードウェア(Trusted Execution Environment(TEE)、セキュア エレメント(SE)など)にバインドできます。キーに対してこの機能が有効化されていると、そのキーマテリアルがセキュア ハードウェアの外部に漏洩することはありません。Android OS が侵害された場合、または攻撃者がデバイスの内部ストレージを読み取れる場合、攻撃者は Android デバイス上のアプリの Android Keystore キーを使用できる可能性がありますが、デバイスからこれらのキーを抽出することはできません。この機能が有効になるのは、デバイスのセキュア ハードウェアが、キーの使用を承認する機能(キー アルゴリズム、ブロックモード、パディング スキーム、ダイジェスト)の特定の組み合わせをサポートしている場合に限られます。キーに対してこの機能が有効になっているかどうかをチェックするには、キーの
KeyInfo
を取得して、KeyInfo.isInsideSecurityHardware()
の戻り値を調べます。
ハードウェア セキュリティ モジュール
Android 9(API レベル 28)以降がインストールされたサポート対象のデバイスでは、ハードウェアのセキュリティ モジュール内にある Keymaster HAL の実装である StrongBox Keymaster を使用できます。このモジュールには次のものが含まれています。
- 独自の CPU。
- 安全なストレージ。
- 真性乱数ジェネレータ。
- パッケージの改ざんやアプリの不正なサイドローディングを防ぐ追加のメカニズム。
システムでは、StrongBox Keymaster に格納されているキーをチェックするときに、信頼できる実行環境(TEE)でキーの整合性を保証します。
StrongBox の省電力実装をサポートするために、アルゴリズムとキーサイズのサブセットがサポートされています。
- RSA 2048
- AES 128 および 256
- ECDSA P-256
- HMAC-SHA256(8 バイトから 64 バイトまでの範囲のキーサイズをサポート)
- Triple DES 168
KeyStore
クラスを使用してキーの生成やインポートを行う場合は、setIsStrongBoxBacked()
メソッドに true
を渡すことで、StrongBox Keymaster 内にキーを保存するように環境設定を指定します。
キー使用の承認
Android デバイスでキーの不正使用を低減するには、Android Keystore を使用してキーを生成またはインポートするときに、承認するキーの使用法をアプリで指定します。キーを生成またはインポートした後、その承認を変更することはできません。その後、キーが使用されるときは常に、Android Keystore によって承認が適用されます。通常、この高度なセキュリティ機能は、キーを生成またはインポートした後(キーの生成やインポートの前、またはその最中ではない)のアプリプロセスの侵害によりキーの不正使用が発生しないという要件がある場合にのみ有用です。
サポートされるキーの使用承認は、次のカテゴリに分類されます。
- 暗号化: 承認されたキーのアルゴリズム、操作、または目的(暗号化、復号、署名、検証)、パディング スキーム、ブロックモード、およびダイジェスト(これらの組み合わせによりキーの使用が承認されます)。
- 一時的有効期間: キー使用を承認する期間を限定します。
- ユーザー認証: ユーザー認証が最近行われている場合に限り、キーを使用できるようにします。詳細については、キー使用を承認するうえでユーザー認証を要求するをご覧ください。
Android デバイスによっては、キーマテリアルがセキュア ハードウェアの内部にあるキー(KeyInfo.isInsideSecurityHardware()
を参照)に対し、追加のセキュリティ対策として、一部のキー使用承認がセキュア ハードウェアによって適用されることがあります。多くの場合、暗号化承認やユーザー認証承認がセキュア ハードウェアによって適用されます。一時的有効期間承認に関しては、独立したセキュア リアルタイム クロックをセキュア ハードウェアが備えていることがあまりないため、この承認カテゴリがセキュア ハードウェアによって適用されることもあまりありません。
キーのユーザー認証承認がセキュア ハードウェアによって適用されるかどうかについては、KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()
を使用することで照会できます。
キーチェーンと Android Keystore プロバイダのいずれを使用するのか選択する
システムレベルの認証情報が必要な場合は、KeyChain
API を使用します。アプリが KeyChain
API を通じて認証情報の使用をリクエストしてきた場合、ユーザーは、システムが提供する UI を通じて、アプリにアクセスを許可するインストール済み認証情報を選択します。この場合、ユーザーの同意に基づいて、複数のアプリが同一の認証情報セットを使用できるようになります。
他方、個々のアプリが独自の認証情報を保存し、そのアプリだけがアクセスできるようにする場合は、Android Keystore プロバイダを使用します。これにより、そのアプリだけが使用できる認証情報をアプリが管理できるようになると同時に、KeyChain
API を使用したシステムレベル認証情報で実現できるのと同じセキュリティ上のメリットを実現できます。この方法の場合、ユーザーが認証情報を選択する必要はありません。
Android Keystore プロバイダを使用する
この機能を使用するには、標準の KeyStore
クラスと、標準の KeyPairGenerator
クラスまたは KeyGenerator
クラス、そして Android 4.3(API レベル 18)で導入された AndroidKeyStore
プロバイダを使用します。
AndroidKeyStore
は、KeyStore.getInstance(type)
メソッドで使用するための KeyStore
タイプとして登録し、さらに KeyPairGenerator.getInstance(algorithm, provider)
メソッドおよび KeyGenerator.getInstance(algorithm, provider)
メソッドで使用するためのプロバイダとして登録します。
新しい秘密鍵を生成する
新しい PrivateKey
を生成するには、自己署名証明書の初期 X.509 属性も指定する必要があります。
セキュリティ ライブラリには、有効な対称鍵を生成するためのデフォルト実装が用意されています。次のスニペットをご覧ください。
Kotlin
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
Java
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC; String masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);
また、KeyStore.setKeyEntry
を使用することで、後で証明書を、認証局(CA)によって署名された証明書に置き換えることができます。
キーを生成するには、KeyPairGenerator
と KeyPairGeneratorSpec
を使用します。
Kotlin
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ val kpg: KeyPairGenerator = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore" ) val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY ).run { setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) build() } kpg.initialize(parameterSpec) val kp = kpg.generateKeyPair()
Java
/* * Generate a new EC key pair entry in the Android Keystore by * using the KeyPairGenerator API. The private key can only be * used for signing or verification and only with SHA-256 or * SHA-512 as the message digest. */ KeyPairGenerator kpg = KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); kpg.initialize(new KeyGenParameterSpec.Builder( alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) .build()); KeyPair kp = kpg.generateKeyPair();
新しいシークレット鍵を生成する
シークレット鍵を生成するには、新しい秘密鍵を生成する場合と同じプロセスで行います。いずれの場合でも、セキュリティ ライブラリを使用します。
暗号鍵をセキュアにインポートする
Android 9(API レベル 28)以降では、ASN.1 でエンコードされたキー形式を使用して、暗号化されたキーをキーストアに安全にインポートできます。次に、Keymaster によりキーストアでキーが復号されるため、デバイスのホストメモリにキーのコンテンツがプレーン テキストで出現することはありません。このプロセスにより、キーを復号する際のセキュリティを強化できます。
暗号鍵をキーストアにセキュアにインポートする機能をサポートする手順は次のとおりです。
PURPOSE_WRAP_KEY
目的を使用するキーペアを生成します。このキーペアにも構成証明を追加することをおすすめします。信頼できるサーバーまたはマシン上で、
SecureKeyWrapper
に格納する ASN.1 メッセージを生成します。ラッパーは、以下のスキーマを格納します。
KeyDescription ::= SEQUENCE { keyFormat INTEGER, authorizationList AuthorizationList } SecureKeyWrapper ::= SEQUENCE { wrapperFormatVersion INTEGER, encryptedTransportKey OCTET_STRING, initializationVector OCTET_STRING, keyDescription KeyDescription, secureKey OCTET_STRING, tag OCTET_STRING }
WrappedKeyEntry
オブジェクトを作成して、ASN.1 メッセージをバイト配列として渡します。この
WrappedKeyEntry
オブジェクトを、Keystore.Entry
オブジェクトを受け入れるsetEntry()
のオーバーロードに渡します。
キーストア エントリを利用する
AndroidKeyStore
プロバイダは、さまざまな標準 KeyStore
API で使用します。
エントリをリスト表示する
aliases()
メソッドを呼び出すと、キーストア内のエントリをリスト表示できます。
Kotlin
/* * Load the Android KeyStore instance using the * "AndroidKeyStore" provider to list out what entries are * currently stored. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val aliases: Enumeration<String> = ks.aliases()
Java
/* * Load the Android KeyStore instance using the * "AndroidKeyStore" provider to list out what entries are * currently stored. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); Enumeration<String> aliases = ks.aliases();
データに署名し、検証する
データに署名するには、キーストアから KeyStore.Entry
を取得して、sign()
などの Signature
API を使用します。
Kotlin
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry: KeyStore.Entry = ks.getEntry(alias, null) if (entry !is KeyStore.PrivateKeyEntry) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return null } val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run { initSign(entry.privateKey) update(data) sign() }
Java
/* * Use a PrivateKey in the KeyStore to create a signature over * some data. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return null; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initSign(((PrivateKeyEntry) entry).getPrivateKey()); s.update(data); byte[] signature = s.sign();
同様に、データを検証するには、verify(byte[])
メソッドを使用します。
Kotlin
/* * Verify a signature previously made by a PrivateKey in our * KeyStore. This uses the X.509 certificate attached to our * private key in the KeyStore to validate a previously * generated signature. */ val ks = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry if (entry == null) { Log.w(TAG, "Not an instance of a PrivateKeyEntry") return false } val valid: Boolean = Signature.getInstance("SHA256withECDSA").run { initVerify(entry.certificate) update(data) verify(signature) }
Java
/* * Verify a signature previously made by a PrivateKey in our * KeyStore. This uses the X.509 certificate attached to our * private key in the KeyStore to validate a previously * generated signature. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.Entry entry = ks.getEntry(alias, null); if (!(entry instanceof PrivateKeyEntry)) { Log.w(TAG, "Not an instance of a PrivateKeyEntry"); return false; } Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(((PrivateKeyEntry) entry).getCertificate()); s.update(data); boolean valid = s.verify(signature);
キー使用を承認するうえでユーザー認証を要求する
キーを生成する際や、キーを AndroidKeyStore
にインポートする際、ユーザーが認証されている場合に限り、キー使用を承認するように指定できます。ユーザー認証は、セキュアロック画面用の認証情報(パターン、PIN、パスワード、指紋)のサブセットを使用して行います。
この高度なセキュリティ機能が役に立つのは通常、キーの生成やインポートの前や最中ではなくキーの生成やインポート後にアプリプロセスに不正侵入されたときに、キーを使用するためにはユーザー認証要件を回避できないように要求する場合に限られます。
ユーザーが認証されている場合に限りキー使用を承認するには、キーを次の 2 つのモードのいずれかで動作するように設定します。
- ユーザー認証に基づいて、一定期間、キーを使用することを承認します。このモードの場合、ユーザーがセキュアロック画面のロックを解除するか、
KeyguardManager.createConfirmDeviceCredentialIntent
フローを使用してセキュアロック画面認証情報を証明すると、どのキーであっても、直ちにその使用が承認されます。承認の有効期間はキーごとに固有であり、キーの生成やインポート中にsetUserAuthenticationValidityDurationSeconds
を使用して指定します。このモードでは、セキュアロック画面が有効になっている場合に限り、キーの生成やインポートを行うことができます(KeyguardManager.isDeviceSecure()
を参照)。セキュアロック画面が無効化された場合(ユーザー認証のない [None] や [Swipe] などのモードに再設定された場合)や、デバイス管理者などによって強制的にリセットされた場合、キーは永続的に無効になります。 - ユーザー認証によって、1 つのキーに関連付けられた 1 つの暗号処理を承認します。このモードの場合、キーに関連付けられた処理をユーザーが個別に承認する必要があります。現在のところ、この承認を行う唯一の方法は、指紋認証(
FingerprintManager.authenticate
)による承認です。このモードでは、少なくとも 1 つの指紋が登録されている場合に限り、キーの生成やインポートを行うことができます(FingerprintManager.hasEnrolledFingerprints
を参照)。新しい指紋が登録された場合や、すべての指紋の登録が抹消された場合、キーは永続的に無効になります。
サポートされているアルゴリズム
Cipher
アルゴリズム | サポート対象(API レベル) | 注 |
---|---|---|
AES/CBC/NoPadding | 23+ | |
AES/CBC/PKCS7Padding | 23+ | |
AES/CTR/NoPadding | 23+ | |
AES/ECB/NoPadding | 23+ | |
AES/ECB/PKCS7Padding | 23+ | |
AES/GCM/NoPadding | 23+ | 12 バイト長の IV のみがサポートされます。 |
RSA/ECB/NoPadding | 18+ | |
RSA/ECB/PKCS1Padding | 18+ | |
RSA/ECB/OAEPWithSHA-1AndMGF1Padding | 23+ | |
RSA/ECB/OAEPWithSHA-224AndMGF1Padding | 23+ | |
RSA/ECB/OAEPWithSHA-256AndMGF1Padding | 23+ | |
RSA/ECB/OAEPWithSHA-384AndMGF1Padding | 23+ | |
RSA/ECB/OAEPWithSHA-512AndMGF1Padding | 23+ | |
RSA/ECB/OAEPPadding | 23+ |
KeyGenerator
アルゴリズム | サポート対象(API レベル) | 注 |
---|---|---|
AES | 23+ | サポートされるサイズ: 128、192、256 |
HmacSHA1 | 23+ |
|
HmacSHA224 | 23+ |
|
HmacSHA256 | 23+ |
|
HmacSHA384 | 23+ |
|
HmacSHA512 | 23+ |
|
KeyFactory
アルゴリズム | サポート対象(API レベル) | 注 |
---|---|---|
EC | 23+ | サポートされるキー仕様: KeyInfo (秘密鍵のみ)、ECPublicKeySpec (公開鍵のみ)、X509EncodedKeySpec (公開鍵のみ) |
RSA | 23+ | サポートされるキー仕様: KeyInfo (秘密鍵のみ)、RSAPublicKeySpec (公開鍵のみ)、X509EncodedKeySpec (公開鍵のみ) |
KeyStore
KeyStore は、KeyPairGenerator
および KeyGenerator
と同じキータイプをサポートします。KeyPairGenerator
アルゴリズム | サポート対象(API レベル) | 注 |
---|---|---|
DSA | 19~22 | |
EC | 23+ |
API レベル 23 よりも前のバージョンで EC キーを生成するには、 |
RSA | 18+ |
|
Mac
アルゴリズム | サポート対象(API レベル) | 注 |
---|---|---|
HmacSHA1 | 23+ | |
HmacSHA224 | 23+ | |
HmacSHA256 | 23+ | |
HmacSHA384 | 23+ | |
HmacSHA512 | 23+ |
Signature
アルゴリズム | サポート対象(API レベル) | 注 |
---|---|---|
MD5withRSA | 18+ | |
NONEwithECDSA | 23+ | |
NONEwithRSA | 18+ | |
SHA1withDSA | 19~22 | |
SHA1withECDSA | 19+ | |
SHA1withRSA | 18+ | |
SHA1withRSA/PSS | 23+ | |
SHA224withDSA | 20~22 | |
SHA224withECDSA | 20+ | |
SHA224withRSA | 20+ | |
SHA224withRSA/PSS | 23+ | |
SHA256withDSA | 19~22 | |
SHA256withECDSA | 19+ | |
SHA256withRSA | 18+ | |
SHA256withRSA/PSS | 23+ | |
SHA384withDSA | 19~22 | |
SHA384withECDSA | 19+ | |
SHA384withRSA | 18+ | |
SHA384withRSA/PSS | 23+ | |
SHA512withDSA | 19~22 | |
SHA512withECDSA | 19+ | |
SHA512withRSA | 18+ | |
SHA512withRSA/PSS | 23+ |
SecretKeyFactory
アルゴリズム | サポート対象(API レベル) | 注 |
---|---|---|
AES | 23+ | サポートされるキー仕様: KeyInfo |
HmacSHA1 | 23+ | サポートされるキー仕様: KeyInfo |
HmacSHA224 | 23+ | サポートされるキー仕様: KeyInfo |
HmacSHA256 | 23+ | サポートされるキー仕様: KeyInfo |
HmacSHA384 | 23+ | サポートされるキー仕様: KeyInfo |
HmacSHA512 | 23+ | サポートされるキー仕様: KeyInfo |
ブログ記事
ICS でキーストア アクセスを統合する方法については、こちらのブログ記事をご覧ください。