يمكن قبول مستندات التعريف الرقمية في كلّ من عمليات التسجيل داخل التطبيق والتسجيل على الويب. لقبول بيانات الاعتماد من "محفظة Google"، عليك إجراء ما يلي:
- الدمج باستخدام التطبيق أو الويب باتّباع التعليمات المقدَّمة
- يُرجى ملء هذا النموذج لطلب قبول بيانات الاعتماد من "محفظة Google" والموافقة على بنود الخدمة.
المتطلبات الأساسية
لاختبار عرض مستندات التعريف رقميًا، عليك أولاً التسجيل في البرنامج التجريبي المفتوح باستخدام الحساب التجريبي المعني (يجب أن يكون حساب Gmail). بعد ذلك، يُرجى تقديم التفاصيل التالية إلى جهة الاتصال المحدّدة لك في Google.
- رابط بنود الخدمة
- Logo
- الموقع الإلكتروني
- أرقام تعريف حِزم التطبيقات (لدمج تطبيقات Android)
- بما في ذلك إصدارات المطوّرين / تصحيح الأخطاء
- توقيع التطبيق
$ $ANDROID_SDK/build-tools/$BUILD_TOOLS_VERSION/apksigner verify --print-certs -v $APK
- رقم تعريف Gmail الذي تم استخدامه للانضمام إلى الإصدار التجريبي العلني
تنسيقات بيانات الاعتماد المتوافقة
هناك العديد من المعايير المقترَحة التي تحدّد تنسيق البيانات في مستندات هوية الرقمية، وقد اكتسب معياران منها رواجًا كبيرًا في المجال:
- مستندات mdocs: تحدّدها الهيئة الدولية للمواصفات (ISO).
- شهادات w3c القابلة للتحقّق: هي الشهادات التي حدّدتها مؤسسة w3c.
على الرغم من أنّ "مدير بيانات الاعتماد" في Android يتوافق مع التنسيقَين، لا تتيح "محفظة Google" حاليًا سوى استخدام بطاقات التعريف الرقمية المستندة إلى mdoc.
بيانات الاعتماد المتوافقة
تتيح "محفظة Google" نوعَين من بيانات الاعتماد:
- رخصة القيادة الرقمية (mDL)
- بطاقة تعريف الهوية
يمكنك طلب أي من بيانات الاعتماد في مسارك من خلال تغيير مَعلمة واحدة.
تجربة المستخدم
عندما يطلب تطبيق سمات الهوية، تحدث العملية التالية:
اكتشاف بيانات الاعتماد: يبحث التطبيق في محافظ الدفع المتاحة لتحديد بيانات الاعتماد التي يمكنها تلبية الطلب. يعرض Android بعد ذلك أداة اختيار واجهة مستخدم النظام، والتي تعرض المعلومات التي سيتمّت مشاركتها. يتيح ذلك للمستخدم اتخاذ قرار مدروس بشأن بيانات الاعتماد التي سيتم استخدامها.
اختيار المستخدم والتفاعل مع المحفظة: يختار المستخدم بيانات اعتماد، ويشغِّل Android تطبيق المحفظة المقابل لإكمال المعاملة. قد يعرض تطبيق المحفظة شاشة موافقة خاصة به أو يطلب تأكيدًا باستخدام تكنولوجيات المقاييس الحيوية.
النتيجة: في حال موافقة المستخدم، تتم مشاركة بيانات اعتماد الهوية التي تم اختيارها مع التطبيق الذي يطلبها. إذا رفض المستخدم، يتم عرض خطأ.
تنسيق الطلب لطلب بيانات اعتماد مستند التعريف من المحفظة
في ما يلي نموذج لطلب requestJson
مستند mdoc للحصول على بيانات اعتماد الهوية
من أي محفظة على جهاز Android أو على الويب.
{
"providers": [{
"protocol": "openid4vp",
"request": "<credential_request>"
}]
}
طلب التشفير
يحتوي client_metadata
على المفتاح العام للتشفير لكل طلب.
عليك تخزين المفاتيح الخاصة لكل طلب واستخدامها لمصادقة
والتفويض برمز المرور الذي تتلقّاه من تطبيق المحفظة.
ستتألف المَعلمة credential_request
في requestJson
من
الحقول التالية.
{
"response_type": "vp_token",
"response_mode": "dc_api.jwt",
"nonce": "1234",
"origin": "https://www.website.com", // omit this parameter for app
"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".
في التطبيق
لطلب بيانات اعتماد الهوية من تطبيقات Android، اتّبِع الخطوات التالية:
تعديل التبعيات
في ملف build.gradle الخاص بمشروعك، عدِّل التبعيات ل استخدام Credential Manager (الإصدار التجريبي):
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")
}
ضبط "مدير بيانات الاعتماد"
لضبط عنصر 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. تطبيق "محفظة Google" هو المسؤول عن صياغة هذا الردّ.
مثال:
"response" : {
<encrpted_response>
}
ستعيد توجيه هذا الردّ إلى الخادم للتحقّق من صحته. يمكنك الاطّلاع على خطوات التحقّق من صحة استجابة بيانات الاعتماد.
الويب
لطلب بيانات اعتماد الهوية باستخدام Digital Credentials API على Chrome، عليك الاشتراك في مرحلة التجربة والتقييم لواجهة برمجة التطبيقات Digital Credentials API.
const credentialResponse = await navigator.credentials.get({
digital: {
providers: [{
protocol: "openid4vp",
request: "<credential_request>"
}]
},
})
أرسِل الردّ من واجهة برمجة التطبيقات هذه إلى خادمك للتحقّق من بيانات الاعتماد الردّ.
خطوات التحقّق من استجابة بيانات الاعتماد
عند تلقّي identityToken المشفَّر من تطبيقك أو موقعك الإلكتروني، هناك عدة عمليات تحقق يجب إجراؤها قبل الوثوق بالردّ.
فك تشفير الاستجابة باستخدام المفتاح الخاص
الخطوة الأولى هي فك تشفير الرمز المميّز باستخدام المفتاح الخاص المحفوظ والحصول على ملف JSON لردّ.
مثال:
TinkConfig.register(); // Sample Private Key JWK String privateKeysetJson = '{"crv":"P-256","d":"evjMOTTqWeTKKOrtWxq9kO_a5rHW_ja_wrT6eH7VPzs","kty":"EC","x":"C2Ka4HcNelUIxMjscNRq1GjFamP-fskGjvR6aY9Ac_Q","y":"a1tXuHhBnsGaaFyNzpqADY_Cp39Q56L2VLuADRsWncE"}'; // Read the private keyset for Hybrid Decryption KeysetHandle privateKeysetHandle = CleartextKeysetHandle.read( JsonKeysetReader.withString(privateKeysetJson)); HybridDecrypt decrypt = privateKeysetHandle.getPrimitive(HybridDecrypt.class); // Split the JWE string by the '.' delimiter String[] parts = jweString.split("\\."); if (parts.length != 5) { throw new IllegalArgumentException("Invalid JWE format (expected 5 parts)"); } // The encrypted data is typically in the second part (index 1) byte[] ciphertext = Base64.getUrlDecoder().decode(parts[1]); // Associated data (AAD) is often the third part (index 2) byte[] associatedData = Base64.getUrlDecoder().decode(parts[2]); // Decrypt the ciphertext byte[] decryptedPayloadBytes = decrypt.decrypt(ciphertext, associatedData); String decryptedJsonResponse = new String(decryptedPayloadBytes, StandardCharsets.UTF_8);
سيؤدي
decrypted_json_response
إلى إنشاءvp_token
JSON يحتوي على بيانات الاعتماد.{ "vp_token": { "cred1": "<credential_token>" } }
إنشاء نص الجلسة
الخطوة التالية هي إنشاء SessionTranscript من ISO/IEC 18013-5:2021 باستخدام بنية تسليم خاصة بنظام التشغيل Android أو الويب:
SessionTranscript = [ null, // DeviceEngagementBytes not available null, // EReaderKeyBytes not available [ "OID4VPDCAPIHandover", 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
، يجب التحقّق من صحة DeviceResponse وفقًا للفقرة 9 من معيار ISO/IEC 18013-5:2021. ويشمل ذلك عدة خطوات، مثل:التحقّق من شهادة جهة إصدار الولاية اطّلِع على شهادات IACA الخاصة بالجهة المعتمَدة لإصدار البطاقات.
التحقّق من توقيع MSO (الفقرة 9.1.2 من معيار 18013-5)
احتساب ملخّصات القيم لعناصر البيانات والتحقّق منها (القسم 9.1.2 من 18013-5)
التحقّق من صحة توقيع
deviceSignature
(الفقرة 9.1.3 من المعيار 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
}
اختبار الحلّ
لاختبار الحلّ، عليك إنشاء تطبيق Android المرجعي المفتوح المصدر وتشغيله. في ما يلي خطوات إنشاء تطبيق حامل المرجع وتشغيله:
- استنساخ مستودع التطبيقات المرجعية
- افتح المشروع في استوديو Android.
- أنشئ هدف
appholder
وشغِّله على جهاز Android أو المحاكي.