يشرح هذا الدليل كيفية إعداد جهات تسجيل أدوات التحقّق (VR)، أي مقدّمي خدمات إثبات الهوية ومجمّعيها والمنصات التي تتحقّق من هوية المستخدمين نيابةً عن عدة عملاء نهائيين (الجهات المعتمِدة النهائية)، لمرجع التصديق (CA) وترميز البيانات الوصفية لـ CBOR ودمج واجهة Digital Credentials API.
اختيار المسار الجهة الموثوقة أو جهة التسجيل المسؤولة عن التحقّق أو التحقّق شخصيًا
آلية عمل عملية دمج "أداة التحقّق" مع "أمين السجلّ"
بصفتك "مسؤول التحقّق"، أنت تعمل كـ "هيئة إصدار شهادات" (CA) خاصة بك، وتوقّع طلبات الهوية لـ "الجهات المعتمدة النهائية" (RP) التي تديرها. اختَر أي خطوة للانتقال إلى تفاصيل تنفيذها:
الخطوة أ: ضبط ملف تعريف شهادة CA والبيانات الوصفية لـ CBOR
1. ملف تعريف الشهادة
يجب توقيع الطلبات باستخدام شهادات X.509 v3 غير المخوّلة للتصديق (المرتبطة بمرجع التصديق الجذري المسجَّل في البيئة التجريبية أو بيئة الإنتاج) التي تم إنشاؤها باستخدام P-256 / ECDSA وتحتوي على إضافة Google مخصّصة:
- معرّف الكائن (OID) للإضافة المخصّصة:
1.3.6.1.4.1.11129.10.1 - الأهمية: غير بالغة الأهمية
- المحتوى: تجزئة SHA-256 لـ
RelyingPartyMetadataBytes، بترميز ASN.1OCTET STRING. - الموضوع (الاسم المميّز): يجب أن يكون فريدًا على مستوى العالم لكلّ "طرف موثوق به" نهائي في المراحل اللاحقة توقّع له.
تنطبق على الشهادات غير المخوّلة للتصديق فقط: تنطبق متطلبات الإضافة المخصّصة والموضوع هذه فقط على شهادات الكيان النهائي (غير المخوّلة للتصديق) التي تمثّل الجهات الموثوقة النهائية في المراحل اللاحقة. لا تتطلّب مراجع التصديق الجذر في بيئة الاختبار والإنتاج، وأي مراجع تصديق وسيطة تستخدمها، إضافة معرّف الكائن (OID) المخصّص من Google، ولا تحتاج إلى موضوع خاص بالطرف المعتمد النهائي.

2. مخطط البيانات الوصفية (CBOR)
يجب ترميز البيانات الوصفية لكل جهة موثوقة نهائية تابعة (والعلامة التجارية المجمّعة الاختيارية) بتنسيق CBOR:
; in CDDL for CBOR encoding
; schemaVersion = "v1"
RelyingPartyMetadataBytes = #6.24(bstr .cbor RelyingPartyMetadata)
RelyingPartyMetadata = {
"schema_version": tstr,
"display": DisplayInfo,
"aggregator_info": DisplayInfo ; Optional: include to show your branding alongside the RP
}
DisplayInfo = {
"display_name": tstr,
"logo_uri": tstr, ; See brand guidelines link in following paragraph
"privacy_policy_uri": tstr
}
يجب أن يتّبع logo_uri إرشادات العلامة التجارية في "محفظة Google".
3- دمج البيانات الوصفية لبروتوكول OpenID4VP
When formatting your signed OpenID4VP credential request in Step B,
include the Base64URL-encoded RelyingPartyMetadataBytes in the
gw_rp_metadata_bytes field inside the client_metadata object, and include
your End-RP leaf certificate chain in the JWS x5c header.
التنسيقات وبيانات الاعتماد والإمكانات المتوافقة
تتيح "محفظة 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
- أنشئ 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.
تتضمّن عملية التحقّق هذه أربع خطوات:
- التحقّق من شهادة جهة الإصدار: استخرِج سلسلة شهادات التوقيع الخاصة بجهة الإصدار من
issuerAuthوتحقَّق من صحتها من خلال مقارنتها بشهادات الجذر الموثوق بها الصادرة عن "هيئة إصدار الشهادات الدولية" (IACA). يُرجى الرجوع إلى جهات الإصدار المعتمَدة وشهادات IACA (وإضافة شهادة Utopia Sandbox عند الاختبار باستخدام مستند تعريف تجريبي في وضع الحماية). - التحقّق من توقيع MSO (القسم 9.1.2 من معيار ISO/IEC 18013-5)
- احتساب
ValueDigestsوالتحقّق منه لعناصر البيانات (القسم 9.1.2 من معيار ISO/IEC 18013-5) - التحقّق من توقيع
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 للحصول على مساعدة في تصحيح الأخطاء. |
الأسئلة الشائعة حول الدمج الفني |