Android Keystore — 暗号化プロバイダーであり、暗号化キーを分離実行環境(TEE)で生成・保存します。この環境はオペレーティングシステムからもアクセスできません。AOSP Security Documentation (2025)によると、Google Playのトップ100 Androidアプリの80%以上がトークン保護とデータ暗号化にKeystoreを使用しています。Android Keystoreの理解は、Androidでの安全な鍵保存に不可欠です。
重要なポイント
Android Keystore — Androidプラットフォームのシステムコンポーネントであり、保護された環境で暗号鍵の生成、保存、使用のためのAPIを提供します。ソフトウェア暗号ライブラリ(Bouncy Castle、Conscrypt)とは異なり、Keystoreは秘密鍵が決して分離実行領域を離れないことを保証します。
KeystoreはAndroid 4.3(API 18)でRSAをサポートするソフトウェアプロバイダーとして登場しました。Android 6.0(API 23)以降、KeystoreはKeymaster Hardware Abstraction Layer(HAL)を通じてハードウェアサポートを取得し、互換性のあるデバイスでは暗号操作をTrusted Execution Environment(TEE)に委任します。Android Compatibility Definition Document(2025)によると、Android 9+のすべてのデバイスはTEEまたはStrongBoxを介したハードウェアKeystoreのサポートが必須です。
Keystoreの鍵はエイリアス(alias)で識別されます — 鍵の作成またはロード時に渡される文字列です。Keystoreは鍵の生の素材を取得することを許可しません:Keystoreで作成された鍵の場合、getEncoded()メソッドはnullを返します。これはソフトウェア鍵との根本的な違いです — 攻撃者はデバイスを完全に制御しても秘密鍵を抽出できません。
KeystoreはAndroidの他のセキュリティ機構と統合されています:生体認証(BiometricPrompt)、ファイルレベル暗号化(File-Based Encryption)、SafetyNet/Play Integrity検証機能。鍵は特定の条件で自動的に削除されるように設定できます:パスコード削除時、新しい指紋追加時、または有効期限切れ時。
Android Keystoreのアーキテクチャには、ハードウェア保護の度合いが異なる3つの実装レベルがあります。レベルはデバイスのハードウェア能力に依存します。
TEE(Trusted Execution Environment) — メインOSと同じプロセッサで並行して動作する分離領域です。TEEはARM TrustZone技術を使用し、プロセッサの物理コアをNormal World(Android)とSecure World(TEE)の2つの仮想領域に分割します。Secure WorldのコードはNormal Worldからアクセスできないメモリとペリフェラルにアクセスできます。
アプリがKeystoreを介して暗号操作を呼び出すと、リクエストはKeymaster HALを通じてTEEに送信され、そこで操作がハードウェアによって実行されます。結果はアプリに返されますが、秘密鍵はTEEの保護されたメモリに残ります。TEEはGlobalPlatform TEE Protection Profileに準拠して認定されており、TrustZoneをサポートするプロセッサを搭載したAndroid 9+デバイスに必須です。
TEEはAES/GCM(128、256ビット)、RSA(2048、4096ビット)、EC(P-256、P-384、P-521)、HMAC-SHA256アルゴリズムをサポートします。TEEのパフォーマンスはソフトウェア暗号化より低く(20~40%低下)、一般的な操作(JWT署名、セッションキー復号)のレイテンシは10~50msを超えません。
StrongBox — メインプロセッサから物理的に分離された専用セキュリティチップです。プロセッサ時間をAndroidと共有するTEEとは異なり、StrongBoxは独自のCPU、RAM、True Random Number Generator(TRNG)、保護ストレージ(One-Time Programmable memory)を備えています。StrongBoxはCommon Criteria EAL 4+およびSecure IC Protection Profileで認定されています。
StrongBoxはAndroid 9+搭載デバイスで対応チップが存在する場合に利用可能です(例:Google PixelのTitan M、Samsung GalaxyのKnox)。開発者はKeyGenParameterSpecでsetIsStrongBoxBacked(true)フラグを使用してStrongBoxを有効にします。ハードウェアサポートがない場合、フラグは無視され、KeystoreはTEEに切り替わります。
StrongBoxの制限:限られたアルゴリズムセット(AES-256、EC P-256、HMAC-SHA256)をサポート、操作キューは一度に最大1つ、操作数はチップリソースによって制限されます。StrongBoxは高負荷シナリオには適していません — 頻繁な操作にはTEEを使用し、StrongBoxは重要な鍵(マスター暗号化鍵、署名鍵)にのみ使用してください。
ソフトウェアベースKeystore — TEEやStrongBoxのハードウェアサポートがないデバイスで使用されるソフトウェア実装です。鍵はファイルシステムに暗号化されて保存されますが、秘密鍵は一時的にRAMで復号される可能性があります。ソフトウェアKeystoreは安全性が低く — rootアクセスを持つ攻撃者はメモリ内の鍵を傍受できます。
Android 12(API 31)以降、Googleはすべての新規デバイスにハードウェアKeystoreサポートを義務付けています。Android 9〜11搭載デバイスでは、低価格モデルにソフトウェアKeystoreが搭載されている場合があります。開発者はKeyStore.getKeyCharacteristics()でセキュリティレベルを確認できます — SECURITY_LEVEL_TRUSTED_ENVIRONMENTまたはSECURITY_LEVEL_STRONGBOX属性がハードウェア保護を確認します。
Android Keystoreは鍵の種類に応じて分類された幅広い暗号アルゴリズムをサポートします。アルゴリズムの選択はパフォーマンス、互換性、セキュリティレベルに影響します。
AES(Advanced Encryption Standard) — デバイス上のデータ保護のための対称暗号化。推奨モード:AES/GCM/NoPadding(256ビット)。GCMは認証付き暗号化(AEAD)を提供し、暗号化データの整合性を検証します。GCMのIV(Initialization Vector)サイズ:12バイト。AES/ECBは使用しないでください — 適切な保護を提供しません。
RSA(Rivest–Shamir–Adleman) — セッションキーの保護とデジタル署名のための非対称暗号化。推奨サイズ:2048または4096ビット。モード:RSA/ECB/PKCS1Padding(暗号化)およびRSA/ECB/PKCS1Sign(署名)。RSA 1024は時代遅れとみなされ、新しいアプリケーションには推奨されません(NIST SP 800-131A Rev. 2)。
EC(Elliptic Curve) — 署名と鍵交換のための楕円曲線暗号。サポートされる曲線:secp256r1(P-256、必須)、secp384r1(P-384)、secp521r1(P-521)。ECは大幅に小さい鍵サイズでRSAと同等のセキュリティを提供します。P-256はほとんどのシナリオに推奨されます:すべてのデバイスでサポートされ、128ビットのセキュリティレベルを提供します。
HMAC(Hash-based Message Authentication Code) — メッセージの対称認証。サポートされるハッシュ関数:SHA-256、SHA-384、SHA-512。HMACはデータの整合性と信頼性の検証に使用されます。例:webhookリクエストの検証や設定の整合性チェック。
すべてのアルゴリズムはKeyGenParameterSpec.Builder.setUserAuthenticationRequired(true)を介して生体認証にバインドできます。Android 11+では、生体認証後に再要求なしで鍵が利用可能なタイムアウト(秒単位)を指定するsetUserAuthenticationParameters()フラグが利用可能です。
Android Keystoreを使用したKotlinの実践例を見てみましょう:AES鍵の生成、データの暗号化、署名用の非対称ペアの作成。
この例では、生体認証にバインドされた256ビットAES/GCM鍵を作成します。getEncoded()によるエクスポートはできません。
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyStore
private val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
fun generateAesKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT or
KeyProperties.PURPOSE_DECRYPT
)
.setKeySize(256)
.setBlockModes(KeyProperties.KEY_BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true)
.build()
val generator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore"
)
generator.init(spec)
generator.generateKey()
}
この例では、Android Keystoreの鍵を使用してデータを暗号化します。Cipherがエイリアスで鍵を取得し、AES/GCM暗号化を初期化して、IVとともに暗号化データを返します。
fun encryptData(alias: String, plaintext: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val secretKey = keyStore.getKey(alias, null) as SecretKey
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val iv = cipher.getIV()
val encrypted = cipher.doFinal(plaintext)
// IV + 暗号化データ
return iv + encrypted
}
fun decryptData(alias: String, ciphertextWithIv: ByteArray): ByteArray {
val iv = ciphertextWithIv.copyOfRange(0, 12)
val encrypted = ciphertextWithIv.copyOfRange(12, ciphertextWithIv.size)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val secretKey = keyStore.getKey(alias, null) as SecretKey
val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
return cipher.doFinal(encrypted)
}
この例では、StrongBoxにバインドされたRSA-2048鍵ペアをKeystoreに作成します。秘密鍵は署名に使用され、公開鍵はgetEncoded()でエクスポートできます。
fun generateRsaKeyPair(alias: String) {
val spec = KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN or
KeyProperties.PURPOSE_VERIFY
)
.setKeySize(2048)
.setSignaturePaddings(
KeyProperties.SIGNATURE_PADDING_RSA_PKCS1
)
.setDigests(KeyProperties.DIGEST_SHA256)
.setIsStrongBoxBacked(true)
.build()
val pair = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
).apply { init(spec) }
.generateKeyPair()
// 公開鍵はエクスポート可能
val publicKey = pair.public // X509EncodedKeySpec
}
Android Keystoreを効果的に使用するには、パフォーマンスを維持しながら最大限の保護を確保するルールに従う必要があります。
KeyGenParameterSpecは最小限必要なパラメータで使用してください:実際に使用するpurpose、block modes、paddingsのみを指定します。過剰なパラメータ(例:署名のみに使用する鍵にPURPOSE_ENCRYPTを指定)は不要な攻撃ベクトルを作成します。Androidは署名用のダイジェストを明示的に指定することを推奨します — SHA256が最小許容レベル(SHA1は時代遅れ)。
重要な操作には鍵を生体認証にバインドしてください:setUserAuthenticationRequired(true)は、鍵が生体認証後にのみ使用可能であることを保証します。Android 11+では、1回のセッション内で毎回生体認証を要求しないように、タイムアウト(推奨30〜60秒)を指定してsetUserAuthenticationParameters()を使用します。setInvalidatedByBiometricEnrollment(true)は新しい指紋や顔が追加されると自動的に鍵を無効化し、古い生体データによるアクセスを防ぎます。
初期化段階でセキュリティレベルを確認してください:KeyStore.getKeyCharacteristics()を使用してSECURITY_LEVELを特定します。デバイスがソフトウェアKeystore(SECURITY_LEVEL_SOFTWARE)のみをサポートする場合、機能を拒否するか、追加の暗号化(例:ユーザーパスワードによる鍵ラッピング)を使用するかを判断します。StrongBoxが保証されていない場合はそれに依存せず、常にsetIsStrongBoxBacked(true)フラグを指定し、getKeyCharacteristicsで結果を確認してください。
鍵を定期的に更新してください:暗号鍵には推奨される有効期間があります。NIST SP 800-57はAES鍵を1〜2年ごと、RSA/ECペアを2〜3年ごとに変更することを推奨しています。鍵ローテーションの仕組みを実装してください:アプリ起動時に鍵の作成日時(KeyGenParameterSpec.Builder.setKeyValidityStart/End)を確認し、期限切れ時に新しい鍵を生成します。古い鍵で暗号化された古いデータは復号し、新しい鍵で再暗号化する必要があります。
大規模データにKeystoreを使用しないでください:Keystoreは鍵の保存(数百バイト)を目的としており、大規模ファイルの暗号化には適しません。データ暗号化には次の方式を使用してください:ランダムなAES鍵(DEK — Data Encryption Key)を生成し、データをこの鍵で暗号化し、DEKをKeystore鍵(KEK — Key Encryption Key)で暗号化します。AndroidのEncryptedSharedPreferencesもこの方式を使用しています:マスター鍵がKeystore、データはAES-256 GCMです。
よくある質問
いいえ、Android Keystoreは秘密鍵が決してTEEやStrongBoxから出ないように設計されています。Keystoreで作成された鍵の場合、getEncoded()はnullを返します。鍵はCipher、Signature、Mac APIを介してのみ使用でき、生の素材は利用できません。
TEE(TrustZone) — 同じプロセッサ上の仮想分離で、時間分割を使用します。StrongBoxは独自のCPUとメモリを備えた独立したチップです。StrongBoxはより安全(Common Criteria EAL 4+)ですが、遅く、サポートするアルゴリズムが少ないです。TEEは頻繁な操作に適し、StrongBoxは重要な鍵に適しています。
setIsStrongBoxBacked(true)フラグを付けて鍵を生成した後、KeyStore.getKeyCharacteristics()を使用します。SECURITY_LEVEL_STRONGBOX属性がハードウェアサポートを確認します。デバイスがStrongBoxをサポートしていない場合、KeystoreはエラーなしでTEEに切り替わります — セキュリティレベルを明示的に確認する必要があります。
Keystoreの鍵はデバイスからアプリを削除すると自動的に削除されます。Android 10+では、アプリのマニフェストにallowBackup=trueフラグがある場合、鍵は保持される可能性がありますが、再インストール後は利用できなくなります。クリーンインストールでは鍵を再生成することをお勧めします。
いいえ、Android Keystoreは特定のデバイスのハードウェアにバインドされています。あるデバイスのTEEで生成された鍵を別のデバイスに転送することはできません。クロスプラットフォーム暗号化には次の方式を使用してください:Keystoreがデバイスの鍵を保護し、セッション鍵は非対称暗号化を使用して安全なAPIを介して転送されます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。