在线
应用内流程和网站流程均接受数字身份证件。如需接受 Google 钱包中的凭据,您需要满足以下条件:
- 按照提供的说明通过应用或网站进行集成,并
- 填写此表单,申请接受 Google 钱包中的凭据,并同意相关服务条款。
前提条件
如需测试身份证件展示功能,您必须先使用预期的测试账号注册公开 Beta 版计划。然后,向您的专属 Google 联系人提供后续详细信息。
- 服务条款链接
- 徽标
- 网站
- Play 商店软件包 ID(适用于 Android 应用集成)
- 用于加入公开 Beta 版的 Gmail ID
支持的凭据格式
目前有几种拟议标准定义了数字身份证件的数据格式,其中两种标准在业界获得了广泛的关注:
虽然 Android Credential Manager 支持这两种格式,但 Google 钱包目前仅支持基于 mdoc 的数字身份证件。
用户体验
当应用请求身份属性时,会发生以下流程:
凭据发现:应用会查询可用的钱包,以确定能满足请求的凭据。然后,Android 会显示一个系统界面选择器,其中显示要共享的信息。这样,用户就可以在了解情况的前提下决定使用哪种凭据。
用户选择和钱包互动:用户选择凭据,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 钱包中注册您的应用。