デジタル認証情報のオンラインでの受領

デジタル ID は、アプリ内フローおよびウェブフローでも受け付けることができます。Google ウォレットの認証情報を確認するには、次の要件を満たす必要があります。

  1. 提供された手順に沿ってアプリまたはウェブを使用して統合します。
  2. こちらのフォームに記入して、Google ウォレットからの認証情報の受け入れに関する利用規約をリクエストし、同意します。

前提条件

身分証明書の提示をデジタルでテストするには、まず、対象のテスト アカウント(Gmail アカウントである必要があります)を使用して公開ベータ版プログラムに登録する必要があります。その後、Google の担当者に詳細情報を提供してください。

  • 利用規約のリンク
  • ロゴ
  • ウェブサイト
  • アプリ パッケージ ID(Android アプリの統合用)
    • 開発 / デバッグビルドを含める
  • アプリ署名
    • $ $ANDROID_SDK/build-tools/$BUILD_TOOLS_VERSION/apksigner verify --print-certs -v $APK
  • 公開ベータ版への参加に使用した Gmail ID

サポートされている認証情報の形式

デジタル ID ドキュメントのデータ形式を定義する提案された標準がいくつか存在し、そのうち 2 つが業界で大きな注目を集めています。

  1. mdoc - ISO で定義されています。
  2. w3c 検証可能な認証情報 - w3c によって定義されます。

Android 認証情報マネージャーは両方の形式をサポートしていますが、Google ウォレットは現在、mdoc ベースのデジタル ID のみをサポートしています。

サポートされている認証情報

Google ウォレットは、次の 2 種類の認証情報をサポートしています。

  1. モバイル運転免許証(mDL)
  2. ID パス

1 つのパラメータを変更するだけで、フロー内のいずれかの認証情報をリクエストできます。

ユーザー エクスペリエンス

このセクションでは、推奨されるオンライン プレゼンテーションのフローについて説明します。このフローでは、アルコールの配達のためにアプリに年齢を提示していますが、UX はウェブや他のタイプのプレゼンテーションでも同様です。

アプリまたはウェブサイトで年齢確認を求められた 利用可能な有効な認証情報がユーザーに表示される Google ウォレットに確認ページが表示される ユーザーが認証して共有を確認する アプリまたはウェブサイトに送信されるデータ
アプリまたはウェブサイトで年齢確認を求められた 利用可能な有効な認証情報がユーザーに表示される Google ウォレットに確認ページが表示される ユーザーが認証して共有を確認する アプリまたはウェブサイトに送信されるデータ

主な注意事項

  1. アプリやウェブサイトは、API へのエントリ ポイントの作成方法を柔軟に選択できます。ステップ 1 で説明したように、API 経由で Google ウォレット以外のオプションが利用可能になる見込みがあるため、[デジタル ID で確認] などの汎用的なボタンを表示することをおすすめします。
  2. ステップ 2 の選択ツール画面は Android によってレンダリングされます。有効な認証情報は、各ウォレットが提供する登録ロジックと、信頼できる当事者から送信されたリクエストとの一致によって決定されます。
  3. ステップ 3 は Google ウォレットによってレンダリングされます。Google ウォレットには、デベロッパーが指定した名前、ロゴ、プライバシー ポリシーが表示されます。

デジタル ID フローを追加する

ユーザーが認証情報をお持ちでない場合は、「デジタル ID で確認」ボタンの横にリンクを配置することをおすすめします。このリンクは Google ウォレットにディープリンクし、ユーザーがデジタル ID を追加できるようにします。

アプリまたはウェブサイトで年齢確認を求められた ユーザーが Google ウォレットに移動してデジタル ID を取得する
アプリまたはウェブサイトで年齢確認を求められた ユーザーが Google ウォレットに移動してデジタル ID を取得する

利用できるデジタル ID がない

デジタル ID がないユーザーが [デジタル ID で確認] オプションを選択すると、このエラー メッセージが表示されます。

アプリまたはウェブサイトで年齢確認を求められた お客様にデジタル ID がないことを示すエラーが表示される
アプリまたはウェブサイトで年齢確認を求められた デジタル ID がないことを示すエラーが表示される

この API は、ユーザーのプライバシーを保護するために、ユーザーが利用可能なデジタル ID があるかどうかをサイレントで学習する機能をサポートしていません。そのため、次のようにオンボーディング リンク オプションを含めることをおすすめします。

ウォレットから身分証明書の認証情報をリクエストするリクエスト形式

Android デバイスまたはウェブ上の任意のウォレットから ID 認証情報を取得する mdoc requestJson リクエストの例を次に示します。

{
      "requests" : [
        {
          "protocol": "openid4vp",
          "data": {<credential_request>} // This is an object, shouldn't be a string.
        }
      ]
}

暗号化をリクエストする

client_metadata には、リクエストごとの暗号化公開鍵が含まれています。リクエストごとに秘密鍵を保存し、その鍵を使用してウォレット アプリから受信したトークンを認証および承認する必要があります。

requestJsoncredential_request パラメータは、次のフィールドで構成されます。

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt",
  "nonce": "1234",
  "dcql_query": {
    "credentials": [
      {
        "id": "cred1",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.18013.5.1.mDL"  // this is for mDL. Use com.google.wallet.idcard.1 for ID pass
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ]
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ]
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ]
          }
        ]
      }
    ]
  },
  "client_metadata": {
    "jwks": {
      "keys": [ // sample request encryption key
        {
          "kty": "EC",
          "crv": "P-256",
          "x": "pDe667JupOe9pXc8xQyf_H03jsQu24r5qXI25x_n1Zs",
          "y": "w-g0OrRBN7WFLX3zsngfCWD3zfor5-NLHxJPmzsSvqQ",
          "use": "enc",
          "kid" : "1",
          "alg" : "ECDH-ES",
        }
      ]
    },
    "authorization_encrypted_response_alg": "ECDH-ES",
    "authorization_encrypted_response_enc": "A128GCM"
  }
}

Google ウォレットに保存されている任意の ID 認証情報から、サポートされている属性を任意の数だけリクエストできます。

アプリ内

Android アプリから ID 認証情報をリクエストする手順は次のとおりです。

依存関係を更新する

プロジェクトの build.gradle で、認証情報マネージャー(ベータ版)を使用するように依存関係を更新します。

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)

リクエスト ID 属性

アプリは、ID リクエストに個々のパラメータを指定するのではなく、CredentialOption 内で JSON 文字列としてすべて提供します。認証情報マネージャーは、この JSON 文字列の内容を検証せずに、利用可能なデジタル ウォレットに渡します。各ウォレットは、次の処理を行います。 - JSON 文字列を解析して、ID リクエストを把握します。 - 保存されている認証情報のうち、リクエストを満たすもの(存在する場合)を特定します。

Android アプリの統合の場合でも、サーバーでリクエストを作成することをおすすめします。

リクエスト形式requestJson を使用します。これは、GetDigitalCredentialOption() 関数呼び出しの request で構成されます。

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

レスポンスを検証する

ウォレットからレスポンスが返されたら、レスポンスが成功し、credentialJson レスポンスが含まれていることを確認します。

// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
    val credential = result.credential
    when (credential) {
        is DigitalCredential -> {
            val responseJson = credential.credentialJson
            validateResponseOnServer(responseJson) // make a server call to validate the response
        }
        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.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
    }
}

credentialJson レスポンスには、W3C で定義された暗号化された identityToken(JWT)が含まれています。このレスポンスを作成するのはウォレット アプリです。

例:

"response" : {
  <encrpted_response>
}

このレスポンスをサーバーに渡して、その真正性を確認します。認証情報レスポンスを検証する手順をご覧ください。

ウェブ

Chrome で Digital Credentials API を使用して ID 認証情報をリクエストするには、Digital Credentials API のオリジン トライアルに登録する必要があります。

const credentialResponse = await navigator.credentials.get({
          digital : {
          requests : [
            {
              protocol: "openid4vp",
              data: {<credential_request>} // This is an object, shouldn't be a string.
            }
          ]
        }
      })

この API からのレスポンスをサーバーに送り返して、認証情報レスポンスを検証します。

認証情報レスポンスを検証する手順

アプリまたはウェブサイトから暗号化された identityToken を受け取ったら、レスポンスを信頼する前に複数の検証を行う必要があります。

  1. 秘密鍵を使用してレスポンスを復号する

    最初のステップは、保存した秘密鍵を使用してトークンを復号し、レスポンス JSON を取得することです。

    Python の例:

    from jwcrypto import jwe, jwk
    
    # Retrieve the Private Key from Datastore
    reader_private_jwk = jwk.JWK.from_json(jwe_private_key_json_str)
    
    # Decrypt the JWE encrypted response from Google Wallet
    jwe_object = jwe.JWE()
    jwe_object.deserialize(encrypted_jwe_response_from_wallet)
    jwe_object.decrypt(reader_private_jwk)
    decrypted_payload_bytes = jwe_object.payload
    decrypted_data = json.loads(decrypted_payload_bytes)
    

    decrypted_data は、認証情報が含まれる vp_token JSON になります。

    {
      "vp_token":
      {
        "cred1": "<credential_token>"
      }
    }
    
  2. セッションの文字起こしを作成する

    次のステップでは、Android またはウェブ固有のハンドオーバー構造を使用して、ISO/IEC 18013-5:2021 から SessionTranscript を作成します。

    SessionTranscript = [
      null,                // DeviceEngagementBytes not available
      null,                // EReaderKeyBytes not available
      [
        "OpenID4VPDCAPIHandover",
        AndroidHandoverDataBytes   // BrowserHandoverDataBytes for Web
      ]
    ]
    

    Android とウェブの両方の引き継ぎで、credential_request の生成に使用したノンスを使用する必要があります。

    Android の引き継ぎ

        AndroidHandoverData = [
          origin,             // "android:apk-key-hash:<base64SHA256_ofAppSigningCert>",
          clientId,           // "android-origin:<app_package_name>",
          nonce,              // nonce that was used to generate credential request
        ]
    
        AndroidHandoverDataBytes = hashlib.sha256(cbor2.dumps(AndroidHandoverData)).digest()
        

    ブラウザの引き継ぎ

        BrowserHandoverData =[
          origin,               // Origin URL
          clientId,             // "web-origin:<origin>"
          nonce,               //  nonce that was used to generate credential request
        ]
    
        BrowserHandoverDataBytes = hashlib.sha256(cbor2.dumps(BrowserHandoverData)).digest()
        

    SessionTranscript を使用して、ISO/IEC 18013-5:2021 条項 9 に従って DeviceResponse を検証する必要があります。このプロセスには、次のようなステップが含まれます。

  3. 州発行者の証明書を確認します。サポートされているカード発行会社の IACA 証明書を確認する

  4. MSO 署名を確認する(18013-5 セクション 9.1.2)

  5. データ要素の ValueDigest を計算して確認する(18013-5 セクション 9.1.2)

  6. deviceSignature 署名を検証する(18013-5 セクション 9.1.3)

{
  "version": "1.0",
  "documents": [
    {
      "docType": "org.iso.18013.5.1.mDL",
      "issuerSigned": {
        "nameSpaces": {...}, // contains data elements
        "issuerAuth": [...]  // COSE_Sign1 w/ issuer PK, mso + sig
      },
      "deviceSigned": {
        "nameSpaces": 24(<< {} >>), // empty
        "deviceAuth": {
          "deviceSignature": [...] // COSE_Sign1 w/ device signature
        }
      }
    }
  ],
  "status": 0
}

ソリューションをテストする

ソリューションをテストするには、Google のオープンソース リファレンス ホルダーである Android アプリをビルドして実行します。リファレンス ホルダー アプリをビルドして実行する手順は次のとおりです。

  • リファレンス アプリのリポジトリのクローンを作成します。
  • Android Studio でプロジェクトを開きます。
  • appholder ターゲットをビルドし、Android デバイスまたはエミュレータで実行します。

ゼロ知識証明(ZKP)ベースの検証

ゼロ知識証明(ZKP)は、個人(証明者)が、実際の基盤となるデータ自体を公開することなく、特定の身分証明情報を持っていること、または特定の条件(18 歳以上であること、有効な認証情報を保持していることなど)を検証者に証明できる暗号手法です。本質的には、機密情報を非公開にしながら、本人の身元に関する申告の真偽を確認する方法です。

本人確認データの直接共有に依存するデジタル ID システムでは、多くの場合、ユーザーに過剰な個人情報の共有が求められ、データ侵害やなりすましのリスクが高まります。ZKP はパラダイム シフトをもたらし、開示を最小限に抑えた検証を可能にします。

デジタル ID における ZKP の主なコンセプト:

  • 証明者: 自分の身元の一部を証明しようとしている個人。
  • 検証者: 身分証明書の証明書をリクエストするエンティティ。
  • 証明: 秘密情報を開示することなく、証明者が検証者に主張の真実性を確信させるための暗号化プロトコル。

ゼロ知識証明のコア特性:

  • 完全性: ステートメントが真で、証明者と検証者の両方が誠実であれば、検証者は納得します。
  • 完全性: ステートメントが false の場合、不正な証明者は、正直な検証者にそれが true であると納得させることは(非常に高い確率で)できません。
  • ゼロ知識: 検証者は、文が真であるという事実以外は何も学びません。証明者の ID に関する実際のデータは公開されません。

Google ウォレットからゼロ知識証明を取得するには、リクエスト形式を mso_mdoc_zk に変更し、リクエストzk_system_type を追加する必要があります。

  ...
  "dcql_query": {
    "credentials": [{
      "id": "cred1",
      "format": "mso_mdoc_zk",
      "meta": {
        "doctype_value": "org.iso.18013.5.1.mDL"
        "zk_system_type": [
         {
          "system": "longfellow-libzk-v1",
           "circuit_hash": "2b8e0c49b08eb1801b9bd7a82aa9eb3736a7519fc2b409asdhj1237034", // This will differ if you need more than 1 attribute.
           "num_attributes": 1,
           "version": 1
         }
       ],
       "verifier_message": "challenge"
      },
     "claims": [{
         ...