Android Protected Confirmation

Per aiutarti a confermare le intenzioni degli utenti quando avviano una transazione sensibile, ad esempio un pagamento, i dispositivi supportati con Android 9 (livello API 28) o versioni successive ti consentono di utilizzare Android Protected Confirmation. Quando utilizzi questo flusso di lavoro, la tua app mostra una richiesta all'utente, chiedendogli di approvare una breve affermazione che conferma l'intenzione di completare la transazione sensibile.

Se l'utente accetta la dichiarazione, la tua app può utilizzare una chiave da Android Keystore per firmare il messaggio visualizzato nella finestra di dialogo. La firma indica, con un livello di confidenza molto elevato, che l'utente ha letto l'informativa e l'ha accettata.

Attenzione: la funzionalità di conferma protetta di Android non fornisce un canale di informazioni sicuro per l'utente. La tua app non può assumere alcun impegno di riservatezza oltre a quello offerto dalla piattaforma Android. In particolare, non utilizzare questo flusso di lavoro per visualizzare informazioni sensibili che normalmente non mostreresti sul dispositivo dell'utente.

Dopo che l'utente ha confermato il messaggio, l'integrità del messaggio è garantita, ma la tua app deve comunque usare la crittografia dei dati in transito per proteggere la riservatezza del messaggio firmato.

Per supportare la conferma dell'utente ad alta attendibilità nella tua app, completa i seguenti passaggi:

  1. Genera una chiave di firma asimmetrica utilizzando la classe KeyGenParameterSpec.Builder. Quando crei la chiave, passa true a setUserConfirmationRequired(). Inoltre, chiama setAttestationChallenge(), passando un valore di verifica appropriato fornito dalla relying party.

  2. Registra la chiave appena generata e il certificato di attestazione della chiave con la relying party appropriata.

  3. Invia i dettagli della transazione al tuo server e chiedigli di generare e restituire un oggetto BLOB (Binary Large Object) di dati aggiuntivi. I dati aggiuntivi possono includere i dati da confermare o suggerimenti di analisi, ad esempio le impostazioni internazionali della stringa di prompt.

    Per un'implementazione più sicura, il BLOB deve contenere un nonce cryptographic per la protezione contro gli attacchi di replay e per distinguere le transazioni.

  4. Configura l'oggetto ConfirmationCallback che informa la tua app quando l'utente ha accettato la richiesta visualizzata in una finestra di dialogo di conferma:

    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.
      }
    }

    Se l'utente approva la finestra di dialogo, viene chiamato il callback onConfirmed(). Il BLOB dataThatWasConfirmed è una struttura di dati CBOR che contiene, tra gli altri dettagli, il testo del prompt visualizzato dall'utente e i dati aggiuntivi che hai passato al ConfirmationPrompt generatore. Utilizza la chiave creata in precedenza per firmare il BLOB di dataThatWasConfirmed, quindi ritrasmetti questo BLOB, insieme ai dettagli della firma e della transazione, alla parte coinvolta.

    Per sfruttare appieno la garanzia di sicurezza offerta da Android Protected Confirmation, la parte coinvolta deve eseguire i seguenti passaggi quando riceve un messaggio firmato:

    1. Controlla la firma sul messaggio e la catena di certificati di attestazione della chiave di firma.
    2. Verifica che nel certificato di attestazione sia impostato il flag TRUSTED_CONFIRMATION_REQUIRED, che indica che la chiave di firma richiede la conferma dell'utente attendibile. Se la chiave di firma è una chiave RSA, controlla che non abbia la proprietà PURPOSE_ENCRYPT o PURPOSE_DECRYPT.
    3. Seleziona extraData per assicurarti che questo messaggio di conferma appartenga a una nuova richiesta e non sia ancora stato elaborato. Questo passaggio protegge dagli attacchi di replay.
    4. Analizza promptText per informazioni sull'azione o sulla richiesta confermata. Ricorda che promptText è l'unica parte del messaggio che l'utente ha effettivamente confermato. La parte che si basa sulla dichiarazione non deve mai presumere che i dati da confermare inclusi in extraData corrispondano a promptText.
  5. Aggiungi una logica simile a quella mostrata nello snippet di codice seguente per visualizzare la stessa finestra di dialogo:

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

Risorse aggiuntive

Per ulteriori informazioni su Android Protected Confirmation, consulta le seguenti risorse.

Blog