การยอมรับข้อมูลเข้าสู่ระบบดิจิทัลทางออนไลน์

ระบบยอมรับบัตรประจำตัวดิจิทัลทั้งในโฟลว์ในแอป และเว็บ หากต้องการยอมรับข้อมูลเข้าสู่ระบบจาก Google Wallet คุณจะต้องดำเนินการต่อไปนี้

  1. ผสานรวมโดยใช้แอปหรือเว็บตามวิธีการที่ระบุ
  2. ใช้บัตรประจำตัวทดสอบเพื่อทดสอบโฟลว์โดยใช้แซนด์บ็อกซ์ของ Google Wallet
  3. หากต้องการเผยแพร่ ให้กรอกแบบฟอร์มนี้เพื่อขอสิทธิ์เข้าถึงและยอมรับข้อกำหนดในการให้บริการของข้อมูลเข้าสู่ระบบ Google Wallet คุณต้องกรอกแบบฟอร์มนี้สำหรับนิติบุคคลแต่ละแห่ง ทีมของเราจะติดต่อคุณหลังจากที่คุณกรอกแบบฟอร์ม
  4. หากมีคำถาม โปรดติดต่อwallet-identity-rp-support@google.com

รูปแบบข้อมูลเข้าสู่ระบบที่รองรับ

มีมาตรฐานที่เสนอหลายรายการซึ่งกำหนดรูปแบบข้อมูลของเอกสารระบุตัวตนดิจิทัล โดยมี 2 รายการที่ได้รับความนิยมอย่างมากในอุตสาหกรรม ได้แก่

  1. mdocs - กำหนดโดย ISO
  2. ข้อมูลเข้าสู่ระบบที่ตรวจสอบได้ของ W3C - กำหนดโดย W3C

แม้ว่าเครื่องมือจัดการข้อมูลเข้าสู่ระบบของ Android จะรองรับทั้ง 2 รูปแบบ แต่ปัจจุบัน Google Wallet รองรับเฉพาะบัตรประจำตัวดิจิทัลที่อิงตาม mdoc

ข้อมูลเข้าสู่ระบบที่รองรับ

Google Wallet รองรับข้อมูลเข้าสู่ระบบ 2 ประเภท ได้แก่

  1. ใบขับขี่ดิจิทัล (mDL)
  2. บัตรประจำตัว

คุณขอข้อมูลเข้าสู่ระบบอย่างใดอย่างหนึ่งในโฟลว์ได้โดยเปลี่ยนพารามิเตอร์เพียงรายการเดียว

ประสบการณ์ของผู้ใช้

ส่วนนี้อธิบายขั้นตอนการนำเสนอออนไลน์ที่แนะนำ โฟลว์แสดงการนำเสนออายุต่อแอปสำหรับการนำส่งเครื่องดื่มแอลกอฮอล์ แต่ UX จะคล้ายกันสำหรับเว็บและการนำเสนอประเภทอื่นๆ

ระบบแจ้งให้ผู้ใช้ยืนยันอายุในแอปหรือเว็บไซต์ ผู้ใช้เห็นข้อมูลเข้าสู่ระบบที่มีสิทธิ์ ผู้ใช้เห็นหน้ายืนยันใน Google Wallet ผู้ใช้ตรวจสอบสิทธิ์เพื่อยืนยันการแชร์ ข้อมูลที่ส่งไปยังแอปหรือเว็บไซต์
ระบบแจ้งให้ผู้ใช้ยืนยันอายุในแอปหรือเว็บไซต์ ผู้ใช้เห็นข้อมูลเข้าสู่ระบบที่มีสิทธิ์ ผู้ใช้เห็นหน้ายืนยันใน Google Wallet ผู้ใช้ตรวจสอบสิทธิ์เพื่อยืนยันการแชร์ ข้อมูลที่ส่งไปยังแอปหรือเว็บไซต์

หมายเหตุสำคัญ

  1. แอปหรือเว็บไซต์มีความยืดหยุ่นในการสร้างจุดแรกเข้าสู่ API ดังที่แสดงในขั้นตอนที่ 1 เราขอแนะนำให้แสดงปุ่มทั่วไป เช่น "ยืนยัน ด้วยบัตรประจำตัวดิจิทัล" เนื่องจากเราคาดว่าในอนาคตตัวเลือกอื่นๆ นอกเหนือจาก Google Wallet จะพร้อมใช้งานผ่าน API
  2. หน้าจอตัวเลือกในขั้นตอนที่ 2 แสดงโดย Android ระบบจะพิจารณาข้อมูลเข้าสู่ระบบที่มีสิทธิ์ โดยดูจากการจับคู่ระหว่างตรรกะการลงทะเบียนที่แต่ละ Wallet ระบุไว้กับคำขอที่ส่งโดยผู้ให้บริการที่เชื่อถือได้
  3. ขั้นตอนที่ 3 แสดงผลโดย Google Wallet Google Wallet จะแสดงชื่อ โลโก้ และนโยบายความเป็นส่วนตัวที่นักพัฒนาแอประบุไว้ในหน้าจอนี้

เพิ่มขั้นตอนการใช้บัตรประจำตัวดิจิทัล

หากผู้ใช้ไม่มีข้อมูลเข้าสู่ระบบ เราขอแนะนำให้ระบุลิงก์ ข้างปุ่ม "ยืนยันด้วยบัตรประจำตัวดิจิทัล" ซึ่งจะลิงก์โดยตรงไปยัง Google Wallet เพื่อให้ผู้ใช้เพิ่มบัตรประจำตัวดิจิทัลได้

ระบบแจ้งให้ผู้ใช้ยืนยันอายุในแอปหรือเว็บไซต์ ระบบนำผู้ใช้ไปยัง Google Wallet เพื่อรับบัตรประจำตัวดิจิทัล
ระบบแจ้งให้ผู้ใช้ยืนยันอายุในแอปหรือเว็บไซต์ ระบบนำผู้ใช้ไปยัง Google Wallet เพื่อรับบัตรประจำตัวดิจิทัล

ไม่มีบัตรประจำตัวดิจิทัล

หากผู้ใช้เลือกตัวเลือก "ยืนยันด้วยบัตรประจำตัวดิจิทัล" โดยที่ไม่มีบัตรประจำตัวดิจิทัล ระบบจะแสดงข้อความแสดงข้อผิดพลาดนี้

ระบบแจ้งให้ผู้ใช้ยืนยันอายุในแอปหรือเว็บไซต์ ผู้ใช้จะเห็นข้อผิดพลาดหากไม่มีบัตรประจำตัวดิจิทัล
ระบบแจ้งให้ผู้ใช้ยืนยันอายุในแอปหรือเว็บไซต์ ผู้ใช้จะเห็นข้อผิดพลาดหากไม่มีบัตรประจำตัวดิจิทัล

API ไม่รองรับฟีเจอร์ที่จะเรียนรู้โดยอัตโนมัติว่าผู้ใช้มีบัตรประจำตัวดิจิทัลที่พร้อมใช้งานหรือไม่ เพื่อรักษาความเป็นส่วนตัวของผู้ใช้ ดังนั้น เราขอแนะนำให้ รวมตัวเลือกลิงก์การเริ่มต้นใช้งานตามที่แสดง

รูปแบบคำขอสำหรับการขอข้อมูลเข้าสู่ระบบของบัตรประจำตัวจากกระเป๋าเงิน

นี่คือตัวอย่างคำขอ mdoc requestJson เพื่อรับข้อมูลเข้าสู่ระบบสำหรับระบุตัวตน จากกระเป๋าเงินใดก็ได้ในอุปกรณ์ Android หรือเว็บ

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

ขอการเข้ารหัส

client_metadata มีคีย์สาธารณะสำหรับการเข้ารหัสสำหรับคำขอแต่ละรายการ คุณจะต้องจัดเก็บคีย์ส่วนตัวสำหรับคำขอแต่ละรายการและใช้คีย์ดังกล่าวเพื่อตรวจสอบสิทธิ์ และให้สิทธิ์โทเค็นที่คุณได้รับจากแอป Wallet

พารามิเตอร์ credential_request ใน requestJson มีฟิลด์ต่อไปนี้

ข้อมูลเข้าสู่ระบบที่เฉพาะเจาะจง

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
  "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"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
  },
  "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",  // This is required
          "alg" : "ECDH-ES",  // This is required
        }
      ]
    },
    "vp_formats_supported": {
      "mso_mdoc": {
        "deviceauth_alg_values": [
          -7
        ],
        "isserauth_alg_values": [
          -7
        ]
      }
    }
  }
}

ข้อมูลเข้าสู่ระบบที่มีสิทธิ์

ตัวอย่างคำขอสำหรับทั้ง mDL และบัตรประจำตัวมีดังนี้ ผู้ใช้สามารถดำเนินการต่อ ด้วยวิธีใดวิธีหนึ่ง

{
  "response_type": "vp_token",
  "response_mode": "dc_api.jwt", // change this to dc_api if you want to demo with a non encrypted response.
  "nonce": "1234",
  "dcql_query": {
    "credentials": [
      {
        "id": "mdl-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.18013.5.1.mDL"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      },
      {  // Credential type 2
        "id": "id_pass-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "com.google.wallet.idcard.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.18013.5.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.18013.5.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
    credential_sets : [
      {
        "options": [
          [ "mdl-request" ],
          [ "id_pass-request" ]
        ]
      }
    ]
  },
  "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",  // This is required
          "alg" : "ECDH-ES",  // This is required
        }
      ]
    },
    "vp_formats_supported": {
      "mso_mdoc": {
        "deviceauth_alg_values": [
          -7
        ],
        "isserauth_alg_values": [
          -7
        ]
      }
    }
  }
}

คุณขอแอตทริบิวต์ที่รองรับ จำนวนเท่าใดก็ได้จากข้อมูลเข้าสู่ระบบเพื่อยืนยันตัวตนที่จัดเก็บไว้ใน Google Wallet

คำขอที่ลงชื่อ

เพื่อเพิ่มความปลอดภัยและรับประกันความสมบูรณ์ของคำขอการตรวจสอบสิทธิ์ คุณควรใช้คำขอที่ลงชื่อ (หรือที่เรียกว่าคำขอการให้สิทธิ์ที่ปลอดภัยด้วย JWT หรือ JAR) โปรดดูรายละเอียดเพิ่มเติมในเอกสารประกอบ OpenID4VP

คำขอที่ลงนามจะห่อหุ้มคำขอการนำเสนอที่ตรวจสอบได้ไว้ภายในโทเค็นเว็บ JSON (JWT) ที่ลงนามแบบเข้ารหัส ซึ่งแตกต่างจากคำขอที่ไม่ได้ลงนามซึ่งจะส่งพารามิเตอร์เป็น JSON ดิบ ซึ่งมีประโยชน์หลัก 2 ประการดังนี้

  1. ความสมบูรณ์: ตรวจสอบว่าคำขอไม่มีการดัดแปลง
  2. กลไกการตรวจสอบสิทธิ์ทางเลือก (PKI x509): พิสูจน์ให้ Google Wallet ทราบว่าคำขอมาจากผู้ตรวจสอบที่เป็นเจ้าของคีย์สาธารณะที่เฉพาะเจาะจงซึ่งลงทะเบียนกับ Google Wallet

ข้อกำหนดเบื้องต้น

ก่อนที่จะใช้การเปลี่ยนแปลงโค้ดสำหรับคำขอที่ลงนาม โปรดตรวจสอบว่าคุณมีสิ่งต่อไปนี้

  • คีย์ส่วนตัว: คุณต้องมีคีย์ส่วนตัว (เช่น Elliptic Curve ES256 หรือ RSA) เพื่อลงนามในคำขอที่จัดการในเซิร์ฟเวอร์
  • ใบรับรอง: คุณต้องมีใบรับรอง X.509 มาตรฐานที่ได้จากคู่คีย์
  • การลงทะเบียน: ตรวจสอบว่าได้ลงทะเบียนใบรับรองสาธารณะกับ Google Wallet แล้ว โปรดติดต่อทีมสนับสนุนที่ wallet-identity-rp-support@google.com

ตรรกะการสร้างคำขออัปเดต

การเปลี่ยนแปลงหลักๆ คือการใช้คีย์ส่วนตัวและรวมเพย์โหลดไว้ใน JWS

def construct_openid4vp_request(
    doctypes: list[str],
    requested_fields: list[dict],
    nonce_base64: str,
    jwe_encryption_public_jwk: jwk.JWK,
    is_zkp_request: bool,
    is_signed_request: bool,
    state: dict,
    origin: str
) -> dict:

    # ... [Existing logic to build 'presentation_definition' and basic 'request_payload'] ...

    # ------------------------------------------------------------------
    # SIGNED REQUEST IMPLEMENTATION (JAR)
    # ------------------------------------------------------------------
    if is_signed_request:
        try:
            # 1. Load the Verifier's Certificate
            # We must load the PEM string into a cryptography x509 object
            verifier_cert_obj = x509.load_pem_x509_certificate(
                CERTIFICATE.encode('utf-8'),
                backend=default_backend()
            )

            # 2. Calculate Client ID (x509_hash)
            # We calculate the SHA-256 hash of the DER-encoded certificate.
            cert_der = verifier_cert_obj.public_bytes(serialization.Encoding.DER)
            verifier_fingerprint_bytes = hashlib.sha256(cert_der).digest()

            # Create a URL-safe Base64 hash (removing padding '=')
            verifier_fingerprint_b64 = base64.urlsafe_b64encode(verifier_fingerprint_bytes).decode('utf-8').rstrip("=")

            # Format the client_id as required by the spec
            client_id = f'x509_hash:{verifier_fingerprint_b64}'

            # 3. Update Request Payload with JAR specific fields
            request_payload["client_id"] = client_id

            # Explicitly set expected origins to prevent relay attacks
            if origin:
                request_payload["expected_origins"] = [origin]

            # 4. Create Signed JWT (JWS)
            # Load the signing private key
            signing_key = jwk.JWK.from_pem(PRIVATE_KEY.encode('utf-8'))

            # Initialize JWS with the JSON payload
            jws_token = jws.JWS(json.dumps(request_payload).encode('utf-8'))

            # Construct the JOSE Header
            # 'x5c' (X.509 Certificate Chain) is critical: it allows the wallet
            # to validate your key against the one registered in the console.
            x5c_value = base64.b64encode(cert_der).decode('utf-8')

            protected_header = {
                "alg": "ES256",                 # Algorithm (e.g., ES256 or RS256)
                "typ": "oauth-authz-req+jwt",   # Standard type for JAR
                "kid": "1",                     # Key ID
                "x5c": [x5c_value]              # Embed the certificate
            }

            # Sign the token
            jws_token.add_signature(
                key=signing_key,
                alg=None,
                protected=json_encode(protected_header)
            )

            # 5. Return the Request Object
            # Instead of returning the raw JSON, we return the signed JWT string
            # under the 'request' key.
            return {"request": jws_token.serialize(compact=True)}

        except Exception as e:
            print(f"Error signing OpenID4VP request: {e}")
            return None

    # ... [Fallback for unsigned requests] ...
    return request_payload

ในแอป

หากต้องการขอข้อมูลเข้าสู่ระบบเพื่อยืนยันตัวตนจากแอป Android ให้ทำตามขั้นตอนต่อไปนี้

อัปเดตทรัพยากร Dependency

ใน build.gradle ของโปรเจ็กต์ ให้อัปเดตทรัพยากร Dependency เพื่อ ใช้ Credential Manager (เบต้า) ดังนี้

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-beta01")
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-beta01")
}

กำหนดค่าเครื่องมือจัดการข้อมูลเข้าสู่ระบบ

หากต้องการกำหนดค่าและเริ่มต้นออบเจ็กต์ CredentialManager ให้เพิ่มตรรกะที่คล้ายกับ รายการต่อไปนี้

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

ขอแอตทริบิวต์อัตลักษณ์

แอปจะระบุพารามิเตอร์ทั้งหมดเป็นสตริง JSON ภายใน CredentialOption แทนที่จะระบุพารามิเตอร์แต่ละรายการสำหรับคำขอข้อมูลประจำตัว เครื่องมือจัดการข้อมูลเข้าสู่ระบบจะส่งสตริง JSON นี้ไปยังกระเป๋าเงินดิจิทัลที่พร้อมใช้งานโดยไม่ต้องตรวจสอบเนื้อหา จากนั้นกระเป๋าเงินแต่ละใบจะมีหน้าที่ดังนี้ - แยกวิเคราะห์สตริง JSON เพื่อทำความเข้าใจคำขอระบุตัวตน - การพิจารณาว่าข้อมูลเข้าสู่ระบบที่จัดเก็บไว้รายการใด (หากมี) ตรงตามคำขอ

เราขอแนะนำให้พาร์ทเนอร์สร้างคำขอในเซิร์ฟเวอร์แม้ว่าจะเป็นการผสานรวมแอป Android ก็ตาม

คุณจะใช้ requestJson จากรูปแบบคำขอ เป็น request ในการเรียกใช้ฟังก์ชัน GetDigitalCredentialOption()

// 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 การตอบกลับมี identityToken (JWT) ที่เข้ารหัส ซึ่งกำหนดโดย W3C แอป Wallet มีหน้าที่สร้างคำตอบนี้

ตัวอย่าง

{
  "protocol" : "openid4vp-v1-unsigned",
  "data" : {
    <encrpted_response>
  }
}

คุณจะส่งการตอบกลับนี้กลับไปยังเซิร์ฟเวอร์เพื่อตรวจสอบความถูกต้อง คุณดูขั้นตอนในการตรวจสอบการตอบกลับของข้อมูลเข้าสู่ระบบได้

เว็บ

หากต้องการขอข้อมูลเข้าสู่ระบบโดยใช้ Digital Credentials API ใน Chrome หรือ เบราว์เซอร์อื่นๆ ที่รองรับ ให้ส่งคำขอต่อไปนี้

const credentialResponse = await navigator.credentials.get({
          digital : {
          requests : [
            {
              protocol: "openid4vp-v1-unsigned",
              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)
    # Save public key thumbprint for session transcript
    encryption_public_jwk_thumbprint = reader_private_jwk.thumbprint()
    
    # 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": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec.
      }
    }
    
  2. สร้างข้อความถอดเสียงของเซสชัน

    ขั้นตอนถัดไปคือการสร้าง SessionTranscript จาก ISO/IEC 18013-5:2021 ด้วยโครงสร้างการส่งต่อที่เฉพาะเจาะจงสำหรับ Android หรือเว็บ

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

    สำหรับการส่งต่อทั้งใน Android และเว็บ คุณจะต้องใช้ Nonce เดียวกันกับที่ใช้สร้าง credential_request

    การส่งต่อใน Android

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

    การส่งต่อเบราว์เซอร์

        BrowserHandoverData =[
          origin,               // Origin URL
          nonce,               //  nonce that was used to generate credential request
          encryption_public_jwk_thumbprint,  // Encryption public key (JWK) Thumbprint
        ]
    
        BrowserHandoverDataBytes = hashlib.sha256(cbor2.dumps(BrowserHandoverData)).digest()
        

    เมื่อใช้ SessionTranscript คุณต้องตรวจสอบ DeviceResponse ตาม ข้อ 9 ของ ISO/IEC 18013-5:2021 ซึ่งประกอบด้วยหลายขั้นตอน เช่น

  3. ตรวจสอบใบรับรองผู้ออกบัตรของรัฐ โปรดดูใบรับรอง IACA ของผู้ออกบัตรที่รองรับ

  4. ยืนยันลายเซ็น MSO (18013-5 ส่วนที่ 9.1.2)

  5. คำนวณและตรวจสอบ ValueDigests สำหรับองค์ประกอบข้อมูล (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
}

สร้างโซลูชัน

หากต้องการสร้างโซลูชัน โปรดดูการใช้งานอ้างอิงของเครื่องมือยืนยันตัวตนใน GitHub

การยืนยันโดยใช้ Zero Knowledge Proof (ZKP)

การพิสูจน์แบบไม่เปิดเผยความรู้ (Zero-Knowledge Proof หรือ ZKP) เป็นวิธีการเข้ารหัสที่ช่วยให้บุคคล (ผู้พิสูจน์) พิสูจน์ต่อผู้ตรวจสอบได้ว่าตนมีข้อมูลประจำตัวบางอย่างหรือเป็นไปตามเกณฑ์ที่เฉพาะเจาะจง (เช่น อายุมากกว่า 18 ปี มีข้อมูลรับรองที่ถูกต้อง) โดยไม่ต้องเปิดเผยข้อมูลพื้นฐานที่แท้จริง โดยพื้นฐานแล้ว การยืนยันตัวตนคือวิธีในการยืนยันความจริงของคำกล่าวเกี่ยวกับตัวตนของบุคคล ในขณะที่ยังคงเก็บรายละเอียดที่ละเอียดอ่อนไว้เป็นส่วนตัว

ระบบข้อมูลประจำตัวดิจิทัลที่อาศัยการแชร์ข้อมูลประจำตัวโดยตรงมัก กำหนดให้ผู้ใช้แชร์ข้อมูลส่วนบุคคลมากเกินไป ซึ่งเพิ่มความเสี่ยงต่อ การละเมิดข้อมูลและการขโมยข้อมูลประจำตัว ZKP เป็นการเปลี่ยนแปลงกระบวนทัศน์ที่ช่วยให้ การยืนยันโดยมีการเปิดเผยข้อมูลน้อยที่สุด

แนวคิดหลักของ ZKP ในบัตรประจำตัวดิจิทัล

  • ผู้พิสูจน์: บุคคลที่พยายามพิสูจน์แง่มุมของตัวตน
  • ผู้ยืนยัน: หน่วยงานที่ขอหลักฐานของแอตทริบิวต์ข้อมูลประจำตัว
  • การพิสูจน์: โปรโตคอลการเข้ารหัสที่ช่วยให้ผู้พิสูจน์โน้มน้าว ผู้ยืนยันให้เชื่อว่าคำกล่าวอ้างของตนเป็นความจริงโดยไม่ต้องเปิดเผยข้อมูลลับ

คุณสมบัติหลักของการพิสูจน์แบบไม่เปิดเผยความรู้

  • ความสมบูรณ์: หากข้อความนั้นเป็นจริงและทั้งผู้พิสูจน์และผู้ตรวจสอบมีความซื่อสัตย์ ผู้ตรวจสอบจะเชื่อ
  • ความสมเหตุสมผล: หากข้อความไม่เป็นความจริง ผู้พิสูจน์ที่ไม่ซื่อสัตย์จะไม่สามารถ (มีความเป็นไปได้สูงมาก) โน้มน้าวผู้ตรวจสอบที่ซื่อสัตย์ว่าข้อความนั้นเป็นความจริง
  • ความรู้เป็นศูนย์: ผู้ตรวจสอบจะไม่ทราบอะไรเลยนอกเหนือจากข้อเท็จจริงที่ว่าคำสั่งนั้นเป็นจริง โดยจะไม่มีการเปิดเผยข้อมูลจริงจากตัวตนของผู้พิสูจน์

หากต้องการรับการพิสูจน์แบบ Zero Knowledge จาก Google Wallet คุณต้องเปลี่ยนรูปแบบคำขอเป็น 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": "f88a39e561ec0be02bb3dfe38fb609ad154e98decbbe632887d850fc612fea6f", // This will differ if you need more than 1 attribute.
          "num_attributes": 1, // number of attributes (in claims) this has can support
          "version": 5,
          "block_enc_hash": 4096,
          "block_enc_sig": 2945,
        }
        {
          "system": "longfellow-libzk-v1",
          "circuit_hash": "137e5a75ce72735a37c8a72da1a8a0a5df8d13365c2ae3d2c2bd6a0e7197c7c6", // This will differ if you need more than 1 attribute.
          "num_attributes": 1, // number of attributes (in claims) this has can support
          "version": 6,
          "block_enc_hash": 4096,
          "block_enc_sig": 2945,
        }
       ],
       "verifier_message": "challenge"
      },
     "claims": [{
         ...
      "client_metadata": {
        "jwks": {
          "keys": [ // sample request encryption key
            {
              ...

คุณจะได้รับหลักฐานแบบปกปิดข้อมูลที่เข้ารหัสกลับจากกระเป๋าเงิน คุณสามารถ ตรวจสอบความถูกต้องของหลักฐานนี้กับใบรับรอง IACA ของผู้ออกโดยใช้ ไลบรารี longfellow-zk ของ Google

verifier-service มีเซิร์ฟเวอร์ที่ใช้ Docker ซึ่งพร้อมสำหรับการติดตั้งใช้งานและช่วยให้คุณตรวจสอบ การตอบกลับเทียบกับใบรับรอง IACA ของผู้ออกใบรับรองบางรายได้

คุณสามารถแก้ไข certs.pem เพื่อจัดการใบรับรองผู้ออก IACA ที่ต้องการเชื่อถือได้

คุณติดต่อทีมสนับสนุนทางอีเมลเพื่อขอรายละเอียดเพิ่มเติมได้ที่ wallet-identity-rp-support@google.com