Android Protected の確認

Android 9(API レベル 28)以降を搭載したサポート対象のデバイスでは、Android Protected の確認を使用できます。このワークフローを使用すると、ユーザーに承認を求める短い文をアプリで表示できます。これにより、ユーザーが機密性の高いトランザクション(支払いなど)を完了する意思があることをアプリで再確認できます。

ユーザーがこの文を承認すると、アプリは Android キーストアから取得した鍵を使用して、ダイアログに表示されるメッセージに署名することができます。署名により、ユーザーが確実に文を見て、文に同意したことが示されます。

注意: Android Protected の確認は、ユーザーに対して安全な情報チャンネルを提供しません。アプリでは、Android プラットフォームが提供する機密保持の保証以外は保証されません。特に、このワークフローを使用して、ユーザーのデバイスに通常は表示しない機密情報を表示しないようにしてください。

ユーザーがメッセージを確認すると、その整合性が保証されますが、アプリでは引き続き、転送中のデータを暗号化して、署名されたメッセージの機密性を確保する必要があります。

アプリで確実にユーザー確認を行えるようにするには、次の手順を実行します。

  1. KeyGenParameterSpec.Builder クラスを使用して、非対称署名鍵を生成します。この鍵を生成するときには、setUserConfirmationRequired()true を渡します。また、setAttestationChallenge() を呼び出して、リライング パーティから提供された適切なチャレンジ値を渡します。

  2. 新しく生成された鍵に加えて、鍵の構成証明書と適切なリライング パーティを登録します。

  3. トランザクションの詳細をサーバーに送信し、サーバーでエクストラ データの blob を生成して返します。エクストラ データには、確認対象のデータや解析のヒント(プロンプト文字列の言語 / 地域など)を含めることができます。

    実装の安全性を高め、リプレイ攻撃から保護し、トランザクションを明確にするためには、blob に暗号化ノンスを含める必要があります。

  4. 確認ダイアログに表示されたプロンプトをユーザーが承認したことをアプリに通知する ConfirmationCallback オブジェクトを設定します。

    class MyConfirmationCallback : ConfirmationCallback() {
            override fun onConfirmed(dataThatWasConfirmed: ByteArray?) {
                super.onConfirmed(dataThatWasConfirmed)
                // Sign dataThatWasConfirmed using your generated signing key.
                // By completing this process, you generate a "signed statement".
            }
    
            override fun onDismissed() {
                super.onDismissed()
                // Handle case where user declined the prompt in the
                // confirmation dialog.
            }
    
            override fun onCanceled() {
                super.onCanceled()
                // Handle case where your app closed the dialog before the user
                // could respond to the prompt.
            }
    
            override fun onError(e: Exception?) {
                super.onError(e)
                // Handle the exception that the callback captured.
            }
        }
        

    ユーザーがダイアログを承認すると、onConfirmed() コールバックが呼び出されます。dataThatWasConfirmed blob は CBOR データ構造です。このデータ構造には、ユーザーに表示されたプロンプト テキストや、ConfirmationPrompt ビルダーに渡したエクストラ データなどの詳細情報も含まれます。アプリは前に作成された鍵を使用して、dataThatWasConfirmed blob に署名する必要があります。その後、この blob を署名およびトランザクションの詳細とともにリライング パーティに返す必要があります。

    Android Protected の確認がもたらすセキュリティ アシュアランスを最大限に活用するために、リライング パーティは署名付きメッセージを受け取り次第、以下の手順を実行する必要があります。

    1. 署名鍵の構成証明書チェーンに加え、メッセージでも署名を確認します。
    2. 構成証明書に TRUSTED_CONFIRMATION_REQUIRED フラグが設定されていることを確認します。このフラグは、署名鍵の確認を信頼できるユーザーが行う必要があることを示します。署名鍵が RSA 鍵の場合は、PURPOSE_ENCRYPT または PURPOSE_DECRYPT プロパティが設定されていないことを確認します。
    3. extraData をチェックして、この確認メッセージが新しいリクエストに属していて、まだ処理されていないことを確認します。この手順を行うことで、リプレイ攻撃から保護することができます。
    4. promptText を解析して、確認したアクションやリクエストに関する情報を取得します。promptText は、ユーザーが実際に確認したメッセージのほんの一部です。リライング パーティは、extraData に含まれている確認対象のデータが promptText に対応していると仮定してはなりません。
  5. 次のコード スニペットのようなロジックを追加して、ダイアログを表示します。

    // This data structure varies by app type. This is just an example.
        data class ConfirmationPromptData(val sender: String,
                val receiver: String, val amount: String)
    
        val myExtraData: ByteArray = byteArrayOf()
        val myDialogData = ConfirmationPromptData("Ashlyn", "Jordan", "$500")
        val threadReceivingCallback = Executor { runnable -> runnable.run() }
        val callback = MyConfirmationCallback()
    
        val dialog = ConfirmationPrompt.Builder(context)
                .setPromptText("${myDialogData.sender}, send
                                ${myDialogData.amount} to
                                ${myDialogData.receiver}?")
                .setExtraData(myExtraData)
                .build()
        dialog.presentPrompt(threadReceivingCallback, callback)
        

参考情報

Android Protected の確認について詳しくは、以下のリソースをご覧ください。

ブログ