Чтобы помочь вам подтвердить намерения пользователей при инициировании конфиденциальной транзакции, например, совершении платежа, поддерживаемые устройства под управлением Android 9 (уровень API 28) и выше позволяют использовать функцию Android Protected Confirmation. При использовании этого рабочего процесса ваше приложение отображает пользователю запрос на одобрение в виде краткого заявления, подтверждающего его намерение завершить конфиденциальную транзакцию.
Если пользователь принимает это заявление, ваше приложение может использовать ключ из хранилища ключей Android для подписи сообщения, отображаемого в диалоговом окне. Подпись с высокой степенью уверенности подтверждает, что пользователь ознакомился с заявлением и согласен с ним.
Внимание: Android Protected Confirmation не предоставляет пользователю безопасный канал передачи информации. Ваше приложение не может гарантировать конфиденциальность, превышающую гарантии, предоставляемые платформой Android. В частности, не используйте этот рабочий процесс для отображения конфиденциальной информации, которую вы обычно не отображаете на устройстве пользователя.
После того как пользователь подтвердит сообщение, его целостность будет гарантирована, но ваше приложение по-прежнему должно использовать шифрование данных при передаче, чтобы защитить конфиденциальность подписанного сообщения.
Чтобы обеспечить поддержку высоконадежного подтверждения пользователя в вашем приложении, выполните следующие действия:
Сгенерируйте асимметричный ключ подписи с помощью класса
KeyGenParameterSpec.Builder. При создании ключа передайтеtrueвsetUserConfirmationRequired(). Также вызовитеsetAttestationChallenge(), передав соответствующее значение вызова, предоставленное проверяющей стороной.Зарегистрируйте вновь сгенерированный ключ и сертификат подтверждения вашего ключа у соответствующей проверяющей стороны.
Отправьте данные о транзакции на свой сервер и дайте ему возможность сгенерировать и вернуть большой двоичный объект (BLOB) с дополнительными данными . Дополнительные данные могут включать данные, требующие подтверждения, или подсказки для анализа, например, локаль строки запроса.
Для более безопасной реализации BLOB-объект должен содержать криптографический одноразовый номер для защиты от атак повторного воспроизведения и устранения неоднозначности транзакций.
Настройте объект
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 // responded to the prompt. } override fun onError(e: Exception?) { super.onError(e) // Handle the exception that the callback captured. } }
Ява
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. } }
Если пользователь одобряет диалог, вызывается обратный вызов
onConfirmed(). BLOB-объектdataThatWasConfirmed— это структура данных CBOR , которая содержит, помимо прочего, текст подсказки, увиденный пользователем, а также дополнительные данные, переданные вами в конструкторConfirmationPrompt. Используйте ранее созданный ключ для подписи BLOB-объектаdataThatWasConfirmed, а затем передайте этот BLOB-объект вместе с подписью и данными транзакции обратно проверяющей стороне.Чтобы в полной мере воспользоваться гарантиями безопасности, которые предлагает Android Protected Confirmation, проверяющая сторона должна выполнить следующие шаги после получения подписанного сообщения:
- Проверьте подпись под сообщением, а также цепочку сертификатов подтверждения ключа подписи.
- Убедитесь, что у сертификата подтверждения установлен флаг
TRUSTED_CONFIRMATION_REQUIRED, указывающий на необходимость подтверждения ключа подписи доверенным пользователем. Если ключ подписи — это ключ RSA, проверьте, что у него нет свойствPURPOSE_ENCRYPTилиPURPOSE_DECRYPT. - Проверьте
extraData, чтобы убедиться, что это подтверждающее сообщение относится к новому запросу и ещё не обработано. Этот шаг защищает от атак повторного воспроизведения. - Проанализируйте
promptTextна предмет информации о подтверждённом действии или запросе. Помните, чтоpromptText— это единственная часть сообщения, которую пользователь фактически подтвердил. Проверяющая сторона ни при каких обстоятельствах не должна предполагать, что данные, подлежащие подтверждению, включённые вextraDataсоответствуютpromptText.
Добавьте логику, аналогичную показанной в следующем фрагменте кода, для отображения самого диалогового окна:
Котлин
// 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)
Ява
// 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);
Дополнительные ресурсы
Дополнительную информацию о подтверждении защищенной версии Android можно найти в следующих ресурсах.