正在接受 Google 钱包中的身份证件

在线

应用内流程和网站流程均接受数字身份证件。如需接受 Google 钱包中的凭据,您需要满足以下条件:

  1. 按照提供的说明通过应用或网站进行集成,并
  2. 填写此表单,申请接受 Google 钱包中的凭据,并同意相关服务条款。

前提条件

如需测试身份证件展示功能,您必须先使用预期的测试账号注册公开 Beta 版计划。然后,向您的专属 Google 联系人提供后续详细信息。

  • 服务条款链接
  • 徽标
  • 网站
  • Play 商店软件包 ID(适用于 Android 应用集成)
  • 用于加入公开 Beta 版的 Gmail ID

支持的凭据格式

目前有几种拟议标准定义了数字身份证件的数据格式,其中两种标准在业界获得了广泛的关注:

  1. mdocs - 由 ISO 定义。
  2. W3C 可验证凭据 - 由 W3C 定义。

虽然 Android Credential Manager 支持这两种格式,但 Google 钱包目前仅支持基于 mdoc 的数字身份证件。

用户体验

当应用请求身份属性时,会发生以下流程:

  1. 凭据发现:应用会查询可用的钱包,以确定能满足请求的凭据。然后,Android 会显示一个系统界面选择器,其中显示要共享的信息。这样,用户就可以在了解情况的前提下决定使用哪种凭据。

  2. 用户选择和钱包互动:用户选择凭据,Android 会调用相应的钱包应用来完成交易。钱包应用可能会显示自己的意见征求界面或要求进行生物识别确认。

结果:如果用户同意,系统会与发出请求的应用共享所选的身份凭据。如果用户拒绝,系统会返回错误。

应用内

如需从 Android 应用请求身份凭据,请按以下步骤操作:

更新依赖项

在项目的 build.gradle 中,更新您的依赖项以使用 Credential Manager(Beta 版):

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-beta01")
    // optional - needed for credentials support from play services, for devices running Android 13 and below.
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}

配置 Credential Manager

如需配置和初始化 CredentialManager 对象,请添加类似于以下内容的逻辑:

// Use your app or activity context to instantiate a client instance of CredentialManager.
val credentialManager = CredentialManager.create(context)

请求身份属性

应用不会为身份请求指定单独的参数,而是会在 CredentialOption 中将所有参数作为 JSON 字符串一起提供。Credential Manager 会将此 JSON 字符串传递给可用的数字钱包,而不会检查其内容。然后,每个钱包负责: - 解析 JSON 字符串以了解身份请求。 - 确定其存储的凭据(如果有)中哪些凭据可以满足请求。

W3C 预计将在 Web API 规范中正式定义此 JSON 请求的结构。不过,请务必注意,该规范目前处于草稿阶段,可能会发生变化。

最初,我们将使用预览版协议从 Google 钱包中获取身份证件。这样一来,我们就可以在 Web API 规范最终确定后开始集成和测试。

以下是预览协议的 mdoc requestJson 示例:

{
  identity: {
    providers: [{
      holder: {
        selector: {
          format: ['mdoc'],
          type: 'org.iso.18013.5.1.mDL',
          fields: [
            'org.iso.18013.5.1.family_name',
            'org.iso.18013.5.1.portrait',
          ]
        },
        params: {
          nonce: '1234',
          readerPublicKey: '<public_key>',
          extraParamAsNeededByWallets: true,
        },
      },
    }]
  }
}
// The request in the JSON format to conform with
// the JSON-ified Digital Credentials API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
    GetDigitalCredentialOption(requestJson = requestJson)

// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
    listOf(digitalCredentialOption)
)

coroutineScope.launch {
    try {
        val result = credentialManager.getCredential(
            context = activityContext,
            request = getCredRequest
        )
        verifyResult(result)
    } catch (e : GetCredentialException) {
        handleFailure(e)
    }
}

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson)
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential ${credential.type}")
        }
    }
}

// Handle failure.
fun handleFailure(e: GetCredentialException) {
  when (e) {
        is GetCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to share the credential.
        }
        is GetCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is NoCredentialException -> {
            // No credential was available.
        }
        is CreateCredentialUnknownException -> {
            // An unknown, usually unexpected, error has occurred. Check the
            // message error for any additional debugging information.
        }
        is CreateCredentialCustomException -> {
            // You have encountered a custom error thrown by the wallet.
            // If you made the API call with a request object that's a
            // subclass of CreateCustomCredentialRequest using a 3rd-party SDK,
            // then you should check for any custom exception type constants
            // within that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}

响应会返回 W3C 定义的 identityToken(JSON 字符串)。钱包应用负责构建此响应。

示例:

{
    "token": "<base64 encoded response>"
}

发送令牌并在服务器上进行处理

收到 identityToken 后,您的应用应将其传输到应用服务器进行验证。第一步是将令牌从 base64 格式解码。生成的字节数组表示 CBOR 数据,该数据遵循以下 CDDL。

CredentialDocument = {
  "version": tstr,       // Set to "ANDROID-HPKE-v1"
  "pkEm": bstr,          // Public key, in uncompressed form
  "cipherText": bstr     // The encrypted data
}

下一步是使用适用于 Android 的接力结构计算 ISO/IEC 18013-5:2021 中的 SessionTranscript

SessionTranscript = [
  null,                // DeviceEngagementBytes not available
  null,                // EReaderKeyBytes not available
  AndroidHandover      // Defined below
]

AndroidHandover = [
  "AndroidHandoverv1", // Version number
  nonce,               // nonce that comes from request
  appId,               // RP package name
  pkRHash,             // The SHA256 hash of the recipient public key
]

cipherText 使用 HPKE 加密。如需对其进行解密,请使用 SessionTranscript 作为额外的身份验证数据,以及之前生成的 EC 私钥和以下设置:

  • KEM:DHKEM(P-256, HKDF-SHA256)
  • KDF:HKDF-SHA256
  • AEAD:AES-128-GCM

生成的明文是 ISO/IEC 18013-5:2021 中定义的 DeviceResponse CBOR 字节。必须根据 ISO/IEC 18013-5:2021 第 9 款对 DeviceResponse 进行验证。这包括几个步骤,例如验证 mdoc 是否来自可信的发行者,以及响应是否由预期设备签名。OpenWallet Foundation Identity Credential 项目中的 DeviceResponseParser 类可用于此验证流程的一部分。

Web

如需在 Chrome 上使用 Digital Credentials API 请求身份凭据,您需要注册 Digital Credentials API 源试用

现场互动

如需接受 Google 钱包中的身份证件,您需要执行以下步骤:

  • 构建或获取读卡器,以接受 ISO 18013-5 定义的身份证件
  • 将 IACA 证书加载到读卡器中,以确保接受的身份证件真实无误
  • 测试您的解决方案
  • 在 Google 钱包中注册您的应用

构建或获取读卡器,以接受 ISO 18013-5 定义的身份证件

Google 钱包中的身份证件是根据移动驾照的 ISO 18013-5 标准实现的。它们使用基于 NFC 或二维码的互动以及 BLE 作为数据传输机制,因此任何能够实现该标准的各个方面的设备都可以充当读卡器,甚至包括移动应用。由于该标准是开放的,因此市场上有多个第三方实现。此外,您还可以根据需要直接实现该功能。

如需有关如何自行实现此功能的指导,请参阅我们的开源参考读取器 Android 应用,该应用实现了 ISO 标准,可以接受来自 Google 钱包的 mDL。

您可以先构建并运行参考文档阅读器应用:

  • 克隆参考应用代码库
  • Android Studio 中打开项目
  • 在 Android 设备或模拟器上构建并运行 appverifier 目标。

将 IACA 证书加载到读卡器中,以确保接受的身份证件真实无误

如需验证真实凭据,您需要在钱包中拥有由受支持的发卡机构签发的身份证件。下面列出了 Google 钱包支持的发卡机构,以及用于验证的证书链接。

测试您的解决方案

如需测试您的解决方案,请构建并运行我们的开源引用持有器 Android 应用。 以下是构建和运行参考持有者应用的步骤:

  • 克隆参考应用代码库
  • Android Studio 中打开项目
  • 在 Android 设备或模拟器上构建并运行 appholder 目标。

(可选)在 Google 钱包中注册您的应用

请填写此表单,在 Google 钱包中注册您的应用。