Чтобы помочь вам подтвердить намерения пользователей при инициировании конфиденциальной транзакции, например, совершении платежа, поддерживаемые устройства под управлением 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 можно найти в следующих ресурсах.