Android 受保护的确认

在搭载 Android 9(API 级别 28)或更高版本的受支持设备上,您可以使用 Android 受保护的确认功能。使用此工作流时,应用会向用户显示提示,请他们批准一个简短的声明。应用可以通过这个声明再次确认,用户确实想完成一项敏感交易(例如付款)。

如果用户接受该声明,应用就可以使用 Android 密钥库中的密钥对对话框中显示的消息进行签名。该签名具有很高的可信度,表示用户已看过声明并同意其内容。

注意:Android 受保护的确认并非为用户提供安全信息通道。应用不能假定存在 Android 平台所提供机密性保证之外的任何其他保证。具体而言,请勿使用该工作流显示您通常不会在用户设备上显示的敏感信息。

用户确认消息后,其完整性将得到保证,但应用必须仍使用传输中的数据加密确保已签署消息的机密性。

如需在应用中为高可信度用户确认提供支持,请完成以下步骤:

  1. 使用 KeyGenParameterSpec.Builder生成非对称签名密钥。创建该密钥时,将 true 传入 setUserConfirmationRequired() 中。此外,调用 setAttestationChallenge() 并传入由依赖方提供的合适私钥保护值。

  2. 向相应的依赖方注册新生成的密钥和密钥的认证证书。

  3. 将交易详情发送至服务器,让其生成并返回一个额外数据 blob。额外数据可能包括待确认数据或解析提示,例如提示字符串的语言区域。

    为了提高实现的安全性,该 blob 必须包含加密随机数以防范重放攻击并消除交易歧义。

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

    如果用户批准该对话框,则调用 onConfirmed() 回调。dataThatWasConfirmed blob 是一个 CBOR 数据结构,其中包含用户看到的提示文本以及您传入 ConfirmationPrompt 构建器的额外数据,还包含其他详细信息。应用应使用之前创建的密钥签署 dataThatWasConfirmed blob。然后,您应该将该 blob 连同签名和交易详情回传给依赖方。

    为了充分利用 Android 受保护的确认提供的安全保障,依赖方必须在收到已签署的消息后执行以下步骤:

    1. 检查消息上的签名以及签名密钥的认证证书链。
    2. 检查认证证书是否设置了 TRUSTED_CONFIRMATION_REQUIRED 标记,这表示签名密钥需要受信任的用户确认。如果签名密钥是 RSA 密钥,请确认它不具有 PURPOSE_ENCRYPTPURPOSE_DECRYPT 属性。
    3. 检查 extraData 以确保此确认消息属于新请求但尚未处理。此步骤可防范重放攻击。
    4. 解析 promptText 以获取有关已确认操作或请求的信息。请谨记,promptText 是用户实际确认的消息的唯一部分。依赖方绝不能假设 extraData 包含的待确认数据对应于 promptText
  5. 添加与以下代码段所示内容类似的逻辑,以显示对话框本身:

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

其他资源

要详细了解 Android 受保护的确认,请参阅以下资源。

博客