Android Keystore システム

Android Keystore システムを使用すると、コンテナに暗号化キーを格納し、デバイスからのキーの抽出をより困難にすることができます。キーストアに格納したキーは、エクスポート不可のキーマテリアルを使った暗号化処理に使用できます。さらに、キーを使用するためにユーザー認証を求めたり、特定の暗号化モードでのみキーを使用できるように制限したりするなど、キーをいつどのように使用するかを制限する機能が提供されています。詳しくは、セキュリティ機能をご覧ください。

Keystore システムは、KeyChain API によって使用されるほか、Android 4.3(API レベル 18)で導入された Android Keystore プロバイダ機能でも使用されます。このドキュメントでは、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 クラスを使用してキーを生成またはインポートするときは、KeyGenParameterSpec.Builder クラスまたは KeyProtection.Builder クラスの setIsStrongBoxBacked() メソッドに true を渡して、StrongBox Keymaster にキーを格納するときの設定を指定します。

キーの使用承認

Android デバイスでキーの不正使用を低減するには、Android Keystore を使用してキーを生成またはインポートするときに、承認するキーの使用法をアプリで指定します。キーを生成またはインポートした後、その承認を変更することはできません。その後、キーが使用されるときは常に、Android Keystore によって承認が適用されます。通常、この高度なセキュリティ機能は、キーを生成またはインポートした後(キーの生成やインポートの前、またはその最中ではない)のアプリプロセスの侵害によりキーの不正使用が発生しないという要件がある場合にのみ有用です。

サポートされるキーの使用承認は、次のカテゴリに分類されます。

  • 暗号化: 承認されたキーのアルゴリズム、操作、または目的(暗号化、復号、署名、検証)、パディング スキーム、ブロックモード、およびダイジェスト(これらの組み合わせによりキーの使用が承認されます)。
  • 一時的な有効期間: キーの使用が承認される期間。
  • ユーザー認証: ユーザーが最近承認されている場合にのみキーを使用できます。キーの使用にはユーザー認証が必要を参照してください。

キーマテリアルが安全なハードウェア内にあるキー(KeyInfo.isInsideSecurityHardware() を参照)の追加のセキュリティ対策として、Android デバイスに応じて、一部のキーの使用承認が安全なハードウェアによって適用される場合があります。多くの場合、暗号化承認およびユーザー認証承認は安全なハードウェアによって適用されます。一時的な有効期間による承認では、通常、独立した安全なリアルタイム クロックが使用されないため、多くの場合、この承認は安全なハードウェアによって適用されません。

キーのユーザー認証承認が安全なハードウェアによって適用されるかどうかについては、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 属性を指定する必要もあります。KeyStore.setKeyEntry を使用して、後で証明書を、認証局(CA)が署名した証明書に置き換えることができます。

キーを生成するには、KeyPairGeneratorKeyPairGeneratorSpec を使用します。

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();
    

新しいシークレット キーの生成

キーを生成するには、KeyGeneratorKeyGenParameterSpec を使用します。

暗号化されたキーの安全なインポート

Android 9(API レベル 28)以降では、ASN.1 でエンコードされたキー形式を使用して、暗号化されたキーをキーストアに安全にインポートできます。次に、Keymaster によりキーストアでキーが復号されるため、デバイスのホストメモリにキーのコンテンツがプレーン テキストで出現することはありません。このプロセスにより、キーを復号する際のセキュリティを強化できます。

キーストアへの暗号化されたキーの安全なインポートをサポートするには、次の手順を実行します。

  1. PURPOSE_WRAP_KEY の目的を使用するキーペアを生成します。このキーペアにも構成証明を追加することをおすすめします。

  2. 信頼できるサーバーまたはマシンで、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
        }
        
  3. WrappedKeyEntry オブジェクトを作成して、ASN.1 メッセージをバイト配列として渡します。

  4. この 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、パスワード、指紋)を使用して認証されます。

通常、この高度なセキュリティ機能は、キーを生成またはインポートした後(キーの生成やインポートの前、またはその最中ではない)のアプリプロセスの侵害によりキーを使用するためのユーザー認証の要件がスキップされないという要件がある場合にのみ有用です。

ユーザーが認証されている場合にのみキーの使用が承認される場合、キーは次のいずれかのモードで機能するように設定されます。

  • ユーザー認証により、キーを一定期間使用することが承認されます。このモードのすべてのキーは、ユーザーが安全なロック画面をロック解除するか、KeyguardManager.createConfirmDeviceCredentialIntent フローを使用して安全なロック画面の認証情報を確認すると即座に使用が承認されます。承認が有効になる期間は各キー固有の期間であり、キーの生成またはインポート中に setUserAuthenticationValidityDurationSeconds を使用して指定されます。こうしたキーは、安全なロック画面が有効になっている場合にのみ(KeyguardManager.isDeviceSecure() を参照)生成またはインポートできます。これらのキーは、安全なロック画面が無効にされると(ユーザーを認証しない [None]、[Swipe] などのモードに再設定されると)、または(デバイスの管理者などによって)強制的にリセットされると永続的に無効になります。
  • ユーザー認証により、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+
  • サポートされるサイズ: 8~1024、8 の倍数である必要がある
  • デフォルト サイズ: 160
HmacSHA224 23+
  • サポートされるサイズ: 8~1024、8 の倍数である必要がある
  • デフォルト サイズ: 224
HmacSHA256 23+
  • サポートされるサイズ: 8~1024、8 の倍数である必要がある
  • デフォルト サイズ: 256
HmacSHA384 23+
  • サポートされるサイズ: 8~1024、8 の倍数である必要がある
  • デフォルト サイズ: 384
HmacSHA512 23+
  • サポートされるサイズ: 8~1024、8 の倍数である必要がある
  • デフォルト サイズ: 512

KeyFactory

アルゴリズム サポート対象(API レベル)
EC 23+ サポートされるキー仕様: KeyInfo(プライベート キーのみ)、ECPublicKeySpec(パブリック キーのみ)、X509EncodedKeySpec(パブリック キーのみ)
RSA 23+ サポートされるキー仕様: KeyInfo(プライベート キーのみ)、RSAPublicKeySpec(パブリック キーのみ)、X509EncodedKeySpec(パブリック キーのみ)

KeyStore

KeyStore は、KeyPairGenerator および KeyGenerator と同じキータイプをサポートします。

KeyPairGenerator

アルゴリズム サポート対象(API レベル)
DSA 19~22
EC 23+
  • サポートされるサイズ: 224、256、384、521
  • サポートされる名前付き曲線: P-224(secp224r1)、P-256(secp256r1 および prime256v1)、P-384(secp384r1)、P-521(secp521r1)

API レベル 23 よりも前のバージョンで EC キーを生成するには、setKeyType(String) でキータイプを "EC" に設定した KeyPairGeneratorSpec で初期化された、RSA 暗号化アルゴリズムの KeyPairGenerator を使用します。この方法を使用して EC 曲線名を指定することはできません。NIST P 曲線は、リクエストされたキーサイズに基づいて自動的に選択されます。

RSA 18+
  • サポートされるサイズ: 512、768、1024、2048、3072、4096
  • サポートされる公開指数: 3、65537
  • デフォルトの公開指数: 65537

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

その他のコードサンプル

Android AsymmetricFingerprintDialogAndroid BasicAndroidKeyStoreAndroid ConfirmCredential の各サンプルで、このページで説明した API の使用例がさらに紹介されています。

ブログの記事

ICS でキーストア アクセスを統合する方法についてのブログエントリをご覧ください。