Hướng dẫn viết app android Informational, Transactional năm 2024

To help you confirm users' intentions when they initiate a sensitive transaction, such as making a payment, supported devices that run Android 9 (API level 28) or higher let you use Android Protected Confirmation. When using this workflow, your app displays a prompt to the user, asking them to approve a short statement that reaffirms their intent to complete the sensitive transaction.

Show

If the user accepts the statement, your app can use a key from Android Keystore to sign the message shown in the dialog. The signature indicates, with very high confidence, that the user has seen the statement and has agreed to it.

Caution: Android Protected Confirmation doesn't provide a secure information channel for the user. Your app can't assume any confidentiality guarantees beyond those that the Android platform offers. In particular, don't use this workflow to display sensitive information that you wouldn't ordinarily show on the user's device.

After the user confirms the message, the message's integrity is assured, but your app must still use data-in-transit encryption to protect the confidentiality of the signed message.

To provide support for high-assurance user confirmation in your app, complete the following steps:

  1. using the`KeyGenParameterSpec.Builder`class. When creating the key, pass true into . Also, call , passing a suitable challenge value provided by the relying party.
  2. Enroll the newly generated key and your key's attestation certificate with the appropriate relying party.
  3. Send transaction details to your server and have it generate and return a binary large object (BLOB) of extra data. Extra data might include the to-be-confirmed data or parsing hints, such as the locale of the prompt string. For a more secure implementation, the BLOB must contain a cryptographic nonce for protection against replay attacks and to disambiguate transactions.
  4. Set up the`ConfirmationCallback`object that informs your app when the user has accepted the prompt shown in a confirmation dialog:

    Kotlin

    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  
          // responded to the prompt.  
      }  
      override fun onError(e: Exception?) {  
          super.onError(e)  
          // Handle the exception that the callback captured.  
      }  
    
    }

    Java

    public class MyConfirmationCallback extends ConfirmationCallback { @Override public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {
      super.onConfirmed(dataThatWasConfirmed);  
      // Sign dataThatWasConfirmed using your generated signing key.  
      // By completing this process, you generate a signed statement.  
    
    } @Override public void onDismissed() {
      super.onDismissed();  
      // Handle case where user declined the prompt in the  
      // confirmation dialog.  
    
    } @Override public void onCanceled() {
      super.onCanceled();  
      // Handle case where your app closed the dialog before the user  
      // responded to the prompt.  
    
    } @Override public void onError(Throwable e) {
      super.onError(e);  
      // Handle the exception that the callback captured.  
    
    } } If the user approves the dialog, the onConfirmed() callback is called. The

    public class MyConfirmationCallback extends ConfirmationCallback { @Override public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {

      super.onConfirmed(dataThatWasConfirmed);  
      // Sign dataThatWasConfirmed using your generated signing key.  
      // By completing this process, you generate a signed statement.  
    
    } @Override public void onDismissed() {
      super.onDismissed();  
      // Handle case where user declined the prompt in the  
      // confirmation dialog.  
    
    } @Override public void onCanceled() {
      super.onCanceled();  
      // Handle case where your app closed the dialog before the user  
      // responded to the prompt.  
    
    } @Override public void onError(Throwable e) {
      super.onError(e);  
      // Handle the exception that the callback captured.  
    
    } } 0 BLOB is a CBOR data structure that contains, among other details, the prompt text that the user saw as well as the extra data that you passed into the public class MyConfirmationCallback extends ConfirmationCallback { @Override public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {
      super.onConfirmed(dataThatWasConfirmed);  
      // Sign dataThatWasConfirmed using your generated signing key.  
      // By completing this process, you generate a signed statement.  
    
    } @Override public void onDismissed() {
      super.onDismissed();  
      // Handle case where user declined the prompt in the  
      // confirmation dialog.  
    
    } @Override public void onCanceled() {
      super.onCanceled();  
      // Handle case where your app closed the dialog before the user  
      // responded to the prompt.  
    
    } @Override public void onError(Throwable e) {
      super.onError(e);  
      // Handle the exception that the callback captured.  
    
    } } 1 builder. Use the previously created key to sign the public class MyConfirmationCallback extends ConfirmationCallback { @Override public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {
      super.onConfirmed(dataThatWasConfirmed);  
      // Sign dataThatWasConfirmed using your generated signing key.  
      // By completing this process, you generate a signed statement.  
    
    } @Override public void onDismissed() {
      super.onDismissed();  
      // Handle case where user declined the prompt in the  
      // confirmation dialog.  
    
    } @Override public void onCanceled() {
      super.onCanceled();  
      // Handle case where your app closed the dialog before the user  
      // responded to the prompt.  
    
    } @Override public void onError(Throwable e) {
      super.onError(e);  
      // Handle the exception that the callback captured.  
    
    } } 0 BLOB, then pass this BLOB, along with the signature and transaction details, back to the relying party. To make full use of the security assurance that Android Protected Confirmation offers, the relying party must perform the following steps upon receiving a signed message:

    1. Check the signature over the message as well as the attestation certificate chain of the signing key.
    2. Check that the attestation certificate has the public class MyConfirmationCallback extends ConfirmationCallback {
       @Override  
       public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {  
           super.onConfirmed(dataThatWasConfirmed);  
           // Sign dataThatWasConfirmed using your generated signing key.  
           // By completing this process, you generate a signed statement.  
       }  
       @Override  
       public void onDismissed() {  
           super.onDismissed();  
           // Handle case where user declined the prompt in the  
           // confirmation dialog.  
       }  
       @Override  
       public void onCanceled() {  
           super.onCanceled();  
           // Handle case where your app closed the dialog before the user  
           // responded to the prompt.  
       }  
       @Override  
       public void onError(Throwable e) {  
           super.onError(e);  
           // Handle the exception that the callback captured.  
       }  
      
      } 3 flag set, which indicates that the signing key requires trusted user confirmation. If the signing key is an RSA key, check that it doesn't have the or property.
    3. Check public class MyConfirmationCallback extends ConfirmationCallback {
       @Override  
       public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {  
           super.onConfirmed(dataThatWasConfirmed);  
           // Sign dataThatWasConfirmed using your generated signing key.  
           // By completing this process, you generate a signed statement.  
       }  
       @Override  
       public void onDismissed() {  
           super.onDismissed();  
           // Handle case where user declined the prompt in the  
           // confirmation dialog.  
       }  
       @Override  
       public void onCanceled() {  
           super.onCanceled();  
           // Handle case where your app closed the dialog before the user  
           // responded to the prompt.  
       }  
       @Override  
       public void onError(Throwable e) {  
           super.onError(e);  
           // Handle the exception that the callback captured.  
       }  
      
      } 6 to make sure that this confirmation message belongs to a new request and hasn't been processed yet. This step protects against replay attacks.
    4. Parse the public class MyConfirmationCallback extends ConfirmationCallback {
       @Override  
       public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {  
           super.onConfirmed(dataThatWasConfirmed);  
           // Sign dataThatWasConfirmed using your generated signing key.  
           // By completing this process, you generate a signed statement.  
       }  
       @Override  
       public void onDismissed() {  
           super.onDismissed();  
           // Handle case where user declined the prompt in the  
           // confirmation dialog.  
       }  
       @Override  
       public void onCanceled() {  
           super.onCanceled();  
           // Handle case where your app closed the dialog before the user  
           // responded to the prompt.  
       }  
       @Override  
       public void onError(Throwable e) {  
           super.onError(e);  
           // Handle the exception that the callback captured.  
       }  
      
      } 7 for information about the confirmed action or request. Remember that the public class MyConfirmationCallback extends ConfirmationCallback {
       @Override  
       public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {  
           super.onConfirmed(dataThatWasConfirmed);  
           // Sign dataThatWasConfirmed using your generated signing key.  
           // By completing this process, you generate a signed statement.  
       }  
       @Override  
       public void onDismissed() {  
           super.onDismissed();  
           // Handle case where user declined the prompt in the  
           // confirmation dialog.  
       }  
       @Override  
       public void onCanceled() {  
           super.onCanceled();  
           // Handle case where your app closed the dialog before the user  
           // responded to the prompt.  
       }  
       @Override  
       public void onError(Throwable e) {  
           super.onError(e);  
           // Handle the exception that the callback captured.  
       }  
      
      } 7 is the only part of the message that the user actually confirmed. The relying party must never assume that to-be confirmed data included in public class MyConfirmationCallback extends ConfirmationCallback {
       @Override  
       public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {  
           super.onConfirmed(dataThatWasConfirmed);  
           // Sign dataThatWasConfirmed using your generated signing key.  
           // By completing this process, you generate a signed statement.  
       }  
       @Override  
       public void onDismissed() {  
           super.onDismissed();  
           // Handle case where user declined the prompt in the  
           // confirmation dialog.  
       }  
       @Override  
       public void onCanceled() {  
           super.onCanceled();  
           // Handle case where your app closed the dialog before the user  
           // responded to the prompt.  
       }  
       @Override  
       public void onError(Throwable e) {  
           super.onError(e);  
           // Handle the exception that the callback captured.  
       }  
      
      } 6 corresponds to the public class MyConfirmationCallback extends ConfirmationCallback {
       @Override  
       public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {  
           super.onConfirmed(dataThatWasConfirmed);  
           // Sign dataThatWasConfirmed using your generated signing key.  
           // By completing this process, you generate a signed statement.  
       }  
       @Override  
       public void onDismissed() {  
           super.onDismissed();  
           // Handle case where user declined the prompt in the  
           // confirmation dialog.  
       }  
       @Override  
       public void onCanceled() {  
           super.onCanceled();  
           // Handle case where your app closed the dialog before the user  
           // responded to the prompt.  
       }  
       @Override  
       public void onError(Throwable e) {  
           super.onError(e);  
           // Handle the exception that the callback captured.  
       }  
      
      } 7.
  5. Add logic similar to that shown in the following code snippet to display the dialog itself:

    Kotlin

    // This data structure varies by app type. This is 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)

    Java

    // This data structure varies by app type. This is an example. class ConfirmationPromptData {
      String sender, receiver, amount;  
      ConfirmationPromptData(String sender, String receiver, String amount) {  
          this.sender = sender;  
          this.receiver = receiver;  
          this.amount = amount;  
      }  
    
    }; final int MY_EXTRA_DATA_LENGTH = 100; byte[] myExtraData = new byte[MY_EXTRA_DATA_LENGTH]; ConfirmationPromptData myDialogData = new ConfirmationPromptData("Ashlyn", "Jordan", "$500"); Executor threadReceivingCallback = Runnable::run; MyConfirmationCallback callback = new MyConfirmationCallback(); ConfirmationPrompt dialog = (new ConfirmationPrompt.Builder(getApplicationContext()))
          .setPromptText("${myDialogData.sender}, send ${myDialogData.amount} to ${myDialogData.receiver}?")  
          .setExtraData(myExtraData)  
          .build();  
    
    dialog.presentPrompt(threadReceivingCallback, callback);

Additional resources

For more information about Android Protected Confirmation, consult the following resources.

Blogs

  • Android Protected Confirmation: Taking transaction security to the next level

Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.

Last updated 2024-01-17 UTC.

[{ "type": "thumb-down", "id": "missingTheInformationINeed", "label":"Missing the information I need" },{ "type": "thumb-down", "id": "tooComplicatedTooManySteps", "label":"Too complicated / too many steps" },{ "type": "thumb-down", "id": "outOfDate", "label":"Out of date" },{ "type": "thumb-down", "id": "samplesCodeIssue", "label":"Samples / code issue" },{ "type": "thumb-down", "id": "otherDown", "label":"Other" }] [{ "type": "thumb-up", "id": "easyToUnderstand", "label":"Easy to understand" },{ "type": "thumb-up", "id": "solvedMyProblem", "label":"Solved my problem" },{ "type": "thumb-up", "id": "otherUp", "label":"Other" }]