Confirmação protegida pelo Android

Com dispositivos compatíveis que executam o Android 9 (API de nível 28) ou versões mais recentes, você pode usar a Confirmação protegida pelo Android. Ao usar esse fluxo de trabalho, o app solicita que o usuário aprove uma breve declaração. Essa declaração permite ao app reafirmar que o usuário quer realizar uma transação confidencial, como fazer um pagamento.

Se o usuário aceitar a declaração, seu app poderá usar uma chave do Android Keystore para assinar a mensagem exibida na caixa de diálogo. A assinatura indica, com confiança muito alta, que o usuário viu a declaração e concordou com ela.

Atenção: a Confirmação protegida pelo Android não oferece um canal de informações seguro ao usuário. O app não pode assumir garantias de confidencialidade além das que a plataforma Android oferece. E, principalmente, não use esse fluxo de trabalho para exibir informações confidenciais que você normalmente não exibiria no dispositivo do usuário.

Quando o usuário confirma a mensagem, a integridade dela está assegurada, mas o app ainda precisa usar criptografia de dados em trânsito para garantir a confidencialidade da mensagem assinada.

Para oferecer compatibilidade com confirmação de alta confiabilidade do usuário no seu app, faça o seguinte:

  1. Gere uma chave de assinatura assimétrica usando a classe KeyGenParameterSpec.Builder. Ao criar a chave, transmita true para setUserConfirmationRequired(). Além disso, chame setAttestationChallenge() transmitindo um valor de desafio adequado fornecido pela parte confiável.

  2. Registre a chave recém-gerada e o certificado de atestado dela junto à parte confiável envolvida.

  3. Envie os detalhes da transação ao seu servidor e faça-o gerar e retornar um blob de dados extras. Dados extras podem incluir os dados a serem confirmados ou dicas de análise, como a localidade da string de solicitação.

    Para uma implementação mais segura, o blob precisa conter um valor de uso único criptográfico para proteção contra ataques de repetição e para eliminar a ambiguidade de transações.

  4. Configure o objeto ConfirmationCallback, que informa ao app quando o usuário aceitou a solicitação exibida em uma caixa de diálogo de confirmação:

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

    Se o usuário aprovar a caixa de diálogo, o callback de onConfirmed() será chamado. O blob dataThatWasConfirmed é uma estrutura de dados CBOR que contém, entre outros detalhes, o texto da solicitação vista pelo usuário, além dos dados extras que você transmitiu no builder ConfirmationPrompt. Seu app precisa usar a chave criada anteriormente para assinar o blob dataThatWasConfirmed. Em seguida, você precisa transmitir o blob, junto com os detalhes da assinatura e da transação, à parte confiável.

    Para aproveitar ao máximo a garantia de segurança oferecida pela Confirmação protegida pelo Android, a parte confiável precisa realizar as seguintes etapas ao receber uma mensagem assinada:

    1. Verificar a assinatura na mensagem, bem como a cadeia de certificados de atestado da chave de assinatura.
    2. Verificar se o certificado de atestado apresenta a sinalização TRUSTED_CONFIRMATION_REQUIRED definida, o que indica que a chave de assinatura requer confirmação de um usuário confiável. Se a chave de assinatura for RSA, ela não poderá ter as propriedades PURPOSE_ENCRYPT e PURPOSE_DECRYPT.
    3. Verifique extraData para ter certeza de que essa mensagem de confirmação pertence a uma nova solicitação e ainda não foi processada. Essa etapa oferece proteção contra ataques de repetição.
    4. Analise se promptText contém informações sobre a ação ou solicitação confirmada. Lembre-se de que promptText é a única parte da mensagem realmente confirmada pelo usuário. A parte confiável nunca deve presumir que os dados a serem confirmados incluídos em extraData correspondem a promptText.
  5. Adicione uma lógica parecida com a mostrada no snippet de código a seguir para exibir a caixa de diálogo:

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

Outros recursos

Para mais informações sobre a Confirmação protegida pelo Android, consulte os recursos a seguir.

Blogs