الخطوة 3: الدمج كجهة معتمِدة

يشرح هذا الدليل كيفية دمج الجهات المعتمِدة المباشرة، مثل التجّار الأفراد والشركات ومطوّري التطبيقات، لواجهة Digital Credentials API لطلب بيانات اعتماد الهوية الرقمية والتحقّق من صحتها في تطبيقات Android والويب.

آلية عمل عملية دمج "الجهة المعتمدة"

تتّبع كل عملية دمج لـ "الجهة المعتمدة" أربع خطوات. يتم تنفيذ الخطوتَين "أ" و"ج" على خادمك، بينما يتم تنفيذ الخطوة "ب" في تطبيق Android أو المتصفّح. اختَر أي خطوة للانتقال إلى تفاصيل تنفيذها:

إنشاء التطبيق واختباره في وضع الحماية قبل طلب بيانات اعتماد الإنتاج

أثناء تنفيذ الخطوات من "أ" إلى "د"، استخدِم مواد العرض في وضع الحماية الموثوق بها مسبقًا من Google لتتمكّن من الإنشاء والاختبار على الفور:

توقيع الطلبات

وقِّع طلبات OpenID4VP باستخدام مفاتيح الاختبار والبيانات الوصفية في وضع الحماية من Google.

بيانات اعتماد الاختبار

اختبِر الميزة على جهاز Android في وضع "وضع الحماية" باستخدام بطاقة هويّة رقمية اختبارية من Utopia.

التحقّق من صحة الردود

التحقّق من صحة issuerAuth من خلال شهادة Utopia Sandbox

التسلسل الكامل بين خادم الخلفية وتطبيق العميل أو المتصفّح و"محفظة Google" لإجراء عملية التحقّق على الإنترنت

التنسيقات وبيانات الاعتماد والإمكانات المتوافقة

تتيح "محفظة Google" استخدام مستندات التعريف الرقمية المستندة إلى ISO mdoc عبر OpenID4VP (الإصدار 1.0) على الإصدار 9 من Android (مستوى واجهة برمجة التطبيقات 28) والإصدارات الأحدث وعلى مستوى المتصفّحات المتوافقة.

بيانات الاعتماد والسمات المتوافقة

يمكنك البحث عن معرّفات doctype_value (رخصة القيادة على الأجهزة الجوّالة، وبطاقة الهوية الرقمية، ومستند التعريف مع صورة) ومسارات طلبات السمات التي يمكنك طلبها من "محفظة Google".

جهات الإصدار المتوافقة وشهادات IACA

نزِّل شهادات الجذر الرسمية الصادرة عن "الرابطة الدولية لمسؤولي إصدار الشهادات" (IACA) للولايات الأمريكية وجهات الإصدار الدولية وجهة إصدار Sandbox Utopia للتحقّق من issuerAuth.


الخطوة أ: تنسيق الطلب وتوقيعه (من جهة الخادم)

لطلب بيانات الاعتماد من أي محفظة، يجب تنسيق طلبك باستخدام OpenID4VP على خادمك. يمكنك طلب بيانات اعتماد معيّنة أو بيانات اعتماد متعددة في عنصر dcql_query واحد.

مثال على طلب JSON

في ما يلي نموذج لطلب mdoc requestJson للحصول على بيانات اعتماد الهوية من أي محفظة على جهاز Android أو الويب.

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

طلب التشفير

يحتوي client_metadata على مفتاح التشفير العام لكل طلب. عليك تخزين المفاتيح الخاصة لكل طلب واستخدامها لمصادقة الرمز المميّز الذي تتلقّاه من تطبيق المحفظة ومنحه الإذن.

البيانات الوصفية المدمجة لبروتوكول OpenID4VP

عند تنسيق طلب بيانات الاعتماد، يجب تضمين الحقل gw_rp_metadata_bytes داخل العنصر client_metadata (كما هو موضّح في نموذج رمز الطلب أدناه). يحتوي هذا الحقل على البيانات الوصفية للجهة المعتمدة والمشفّرة باستخدام Base64URL والتي تطلبها "محفظة Google" لإثبات هويتك وعرض علامتك التجارية للمستخدم.

حمولة طلب بيانات الاعتماد

تحتوي المَعلمة 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": {
          // Use org.iso.18013.5.1.mDL for mDL,
          // com.google.wallet.idcard.1 for ID pass, or
          // org.iso.23220.photoid.1 for ID pass (using photo ID document type)
          "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
          }
        ]
      }
    ]
  },
  "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
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

أي بيانات اعتماد مؤهَّلة

في ما يلي مثال على طلب رخصة 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 pass
        "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 type 3: ID pass (using photo ID document type)
        "id": "photo_id-request",
        "format": "mso_mdoc",
        "meta": {
          "doctype_value": "org.iso.23220.photoid.1"
        },
        "claims": [
          {
            "path": [
              "org.iso.23220.1",
              "family_name"
            ],
            "intent_to_retain": false // set this to true if you are saving the value of the field
          },
          {
            "path": [
              "org.iso.23220.1",
              "given_name"
            ],
            "intent_to_retain": false
          },
          {
            "path": [
              "org.iso.23220.1",
              "age_over_18"
            ],
            "intent_to_retain": false
          }
        ]
      }
    ]
    credential_sets : [
      {
        "options": [
          [ "mdl-request" ],
          [ "id_pass-request" ],
          [ "photo_id-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
        ],
        "issuerauth_alg_values": [
          -7
        ]
      }
    },
    "gw_rp_metadata_bytes": "<base64url encoded metadata string>"
  }
}

يمكنك طلب أي عدد من السمات المتوافقة من أي مستند تعريف هوية مخزَّن في "محفظة Google".

الطلبات الموقَّعة

الطلبات الموقَّعة (طلبات التفويض الآمنة المستندة إلى JWT) تغلّف طلب العرض القابل للتحقّق داخل رمز ويب مميّز بتنسيق JSON (JWT) موقَّع بطريقة مشفّرة باستخدام البنية الأساسية للمفاتيح العامة (PKI)، ما يضمن سلامة الطلب وإثبات هويتك لـ "محفظة Google".

المتطلبات الأساسية

قبل تنفيذ تغييرات الرمز للطلبات الموقَّعة، تأكَّد من توفّر ما يلي:

  • المفتاح الخاص: تحتاج إلى مفتاح خاص (على سبيل المثال، Elliptic Curve ES256) لتوقيع الطلب، وتتم إدارته على خادمك. لاختبار وضع الحماية بصفتك جهة معتمِدة، استخدِم المفتاح الخاص لاختبار وضع الحماية.
  • الشهادة: تحتاج إلى شهادة معيار X.509 مشتقة من مفتاحَي التشفير.
  • التسجيل: تأكَّد من أنّ شهادتك العامة (أو شهادة الجذر لمرجع التصديق لجهات التحقّق من التسجيل) معتمَدة من "محفظة Google".

منطق إنشاء الطلبات

عند إنشاء طلب OpenID4VP وتوقيعه، يجب الانتباه جيدًا إلى تنسيق الترميز الدقيق المتوقّع لكل حقل، لأنّ الخلط بين base64 وbase64url أو استخدام الحشو (=) سيؤدي إلى تعذُّر التحقّق من صحة التوقيع أو المصدر:

الحقل الترميز / التنسيق المطلوب الأحرف والحشو
client_id ‏(x509_hash:<hash>) base64url بدون حشو يستخدم الأبجدية الآمنة لعنوان URL (-، _)، ويتم حذف اللاحقة =
expected_origins ‏(android:apk-key-hash:<hash>) ترميز Base64 العادي بدون حشو يستخدم الأبجدية العادية (+، /)، ويتم تجريد اللاحقة =
expected_origins ‏(https://...) عنوان URL بأحرف صغيرة بدون شَرطات مائلة لاحقة مثال: https://example.com (بدون اللاحقة /)
رأس JWS واحد (x5c) Standard base64 مع مساحة متروكة يستخدم الأبجدية العادية (+، /)، ويحتفظ بحشو = اللاحق

لإنشاء طلب، استخدِم مفتاحك الخاص وأدرِج الحمولة في 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()

            # Important: Use URL-safe Base64 (base64url: '-', '_') without padding ('=') for client_id
            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
            # Important: Use standard Base64 (not URL-safe) without padding for android:apk-key-hash:
            # origin = f"android:apk-key-hash:{base64.b64encode(apk_cert_hash).decode('utf-8').rstrip('=')}"
            # Format for web origin (lowercase URL without trailing slash): origin = "https://example.com"
            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.
            # Important: Use standard Base64 ('+', '/') WITH padding ('=') for x5c
            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

الخطوة ب: تفعيل واجهة برمجة التطبيقات (من جهة العميل)

يجب إنشاء طلب البيانات من واجهة برمجة التطبيقات بالكامل من جهة الخادم. استنادًا إلى المنصة، يمكنك تمرير ملف JSON الذي تم إنشاؤه إلى واجهات برمجة التطبيقات الخاصة بالمنصة.

داخل التطبيق (Android)

لطلب بيانات اعتماد الهوية من تطبيقات Android، اتّبِع الخطوات التالية:

تعديل التبعيات

في ملف build.gradle الخاص بمشروعك، عدِّل التبعيات لاستخدام واجهة Credential Manager (الإصدار التجريبي):

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-beta01")
    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)

طلب سمات الهوية

بدلاً من تحديد مَعلمات فردية لطلبات الهوية، يقدّمها التطبيق معًا كسلسلة JSON ضمن CredentialOption. ينقل Credential Manager سلسلة 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. تطبيق "محفظة Google" هو المسؤول عن إنشاء هذا الردّ.

مثال:

{
  "protocol" : "openid4vp-v1-signed",
  "data" : {
    <encrypted_response>
  }
}

أرسِل هذا الردّ إلى خادمك للتحقّق من صحته. يُرجى الاطّلاع على فك تشفير الردّ والتحقّق من صحته.

الويب

لطلب "مستندات تعريف الهوية" باستخدام Digital Credentials API على Chrome أو متصفّحات أخرى متوافقة، أرسِل الطلب التالي:

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

أرسِل الردّ من واجهة برمجة التطبيقات هذه إلى خادمك للتحقّق من صحة ردّ بيانات الاعتماد.


الخطوة ج: فك تشفير الردّ والتحقّق منه (على جهة الخادم)

بعد أن تعرض المحفظة رمز identityToken (JWT) المشفّر، عليك إجراء عملية التحقّق على صعيد الخادم بشكل صارم قبل الوثوق بالبيانات.

فك تشفير الرد

استخدِم المفتاح الخاص المطابق للمفتاح العام الذي تم إرساله في client_metadata الخاص بالطلب لفك تشفير JWE. يؤدي ذلك إلى vp_token.

مثال على 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 إلى إنشاء ملف JSON بتنسيق vp_token يحتوي على بيانات الاعتماد:

{
  "vp_token":
  {
    "cred1": ["<base64UrlNoPadding_encoded_credential>"] // This applies to OpenID4VP 1.0 spec.
  }
}

إعادة إنشاء SessionTranscript والتحقّق من صحة DeviceResponse

  1. أنشئ 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 Handover

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

Browser Handover

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، يجب التحقّق من صحة "استجابة الجهاز" وفقًا للبند 9 من معيار ISO/IEC 18013-5:2021.

تتضمّن عملية التحقّق هذه أربع خطوات:

  1. التحقّق من شهادة جهة الإصدار: استخرِج سلسلة شهادات التوقيع الخاصة بجهة الإصدار من issuerAuth وتحقَّق من صحتها من خلال مقارنتها بشهادات الجذر الموثوق بها الصادرة عن "هيئة إصدار الشهادات الدولية" (IACA). يُرجى الرجوع إلى جهات الإصدار المعتمَدة وشهادات IACA (وإضافة شهادة Utopia Sandbox عند الاختبار باستخدام مستند تعريف تجريبي في وضع الحماية).
  2. التحقّق من توقيع MSO (القسم 9.1.2 من معيار ISO/IEC 18013-5)
  3. احتساب ValueDigests والتحقّق منه لعناصر البيانات (القسم 9.1.2 من معيار ISO/IEC 18013-5)
  4. التحقّق من توقيع deviceSignature (القسم 9.1.3 من معيار ISO/IEC 18013-5)
{
  "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
}

الخطوة (د) (اختيارية): التحقّق من العمر مع الحفاظ على الخصوصية (ZKP)

لإتاحة "إثباتات المعرفة الصفرية" (على سبيل المثال، التحقّق من أنّ عمر المستخدم يتجاوز 18 عامًا بدون الاطّلاع على تاريخ ميلاده الدقيق)، يجب تغيير تنسيق الطلب إلى mso_mdoc_zk وتقديم إعدادات zk_system_type المطلوبة.

للحصول على نظرة عامة شاملة على ZKP وإمكاناته، يُرجى الاطّلاع على الأسئلة الشائعة.

  ...
  "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.

تحتوي خدمة التحقّق على خادم مستند إلى Docker وجاهز للنشر يتيح لك التحقّق من صحة الردّ مقارنةً بشهادات IACA الصادرة عن جهة الإصدار.

يمكنك تعديل certs.pem لإدارة شهادات جهة إصدار IACA التي تريد الوثوق بها.


حِزم تطوير البرامج (SDK) وعمليات التنفيذ المرجعية والدعم

استخدِم مكتباتنا المفتوحة المصدر والخادم المرجعي وأداة التحقّق من الويب لتسريع عملية التنفيذ:

المراجع / المكتبة ما يوفّره رابط
Identity Verifiers Reference Implementation نموذج لخادم وعميل يوضّحان كيفية إنشاء طلب OpenID4VP وتوقيع JWS وفك تشفير JWE والتحقّق من صحة استجابة mdoc. مستودع GitHub
حزمة تطوير البرامج (SDK) لـ Multipaz مكتبة مفتوحة المصدر (OpenWallet Foundation) تتضمّن DeviceResponseParser لفك ترميز CBOR وإعادة إنشاء SessionTranscript والتحقّق من صحة توقيع ISO/IEC 18013-5/7 openwallet-foundation/multipaz
مكتبة Longfellow ZKP وخدمة Verifier مكتبة مفتوحة المصدر وخادم مستند إلى Docker للتحقّق من صحة بيانات العمر والسمات المستندة إلى "إثبات المعرفة الصفرية" (mso_mdoc_zk). google/longfellow-zk
أداة التحقّق التفاعلية من الويب أداة التحقّق من الويب المستضافة لاختبار طلبات W3C Digital Credentials API / OpenID4VP في "محفظة Google". verifier.multipaz.org
مواصفات OpenID4VP 1.0 المواصفات الرسمية لبروتوكول OpenID for Verifiable Presentations 1.0. مواصفات OpenID4VP
الأسئلة الشائعة ودعم المطوّرين إجابات عن الأسئلة الشائعة حول الدمج الفني، أو يمكنك إرسال رسالة إلكترونية إلى wallet-identity-rp-support@google.com للحصول على مساعدة في تصحيح الأخطاء. الأسئلة الشائعة حول الدمج الفني

الخطوة التالية