במדריך הזה מוסבר איך צדדים מסתמכים (RP) ומפתחי קוראים יכולים להטמיע אימות פנים אל פנים (אופליין) של מסמכים דיגיטליים שמוצגים מ-Google Wallet בהתאם לתקן הבינלאומי ISO/IEC 18013-5.
אפשר לאמת באופן מאובטח אמצעי זיהוי דיגיטליים ב-Google Wallet בסביבות פיזיות (כמו מסופי נקודות מכירה, מקומות לאירועים, שערים לתחבורה ציבורית, קוראים של רשויות אכיפת החוק ואפליקציות קוראות לנייד) בלי שיהיה צורך בחיבור אינטרנט פעיל בזמן ההצגה.
סקירה כללית על הצגה במצב אופליין (ISO/IEC 18013-5)
תקן ISO/IEC 18013-5 מגדיר פרוטוקול סטנדרטי שניתן להפעלה הדדית להצגה במצב אופליין בין המחזיק (המכשיר הנייד של המשתמש שבו פועלת Google Wallet) לבין הקורא או המאמת (מסוף פיזי או אפליקציה נלווית לנייד).
תהליך הצגת המצגת מתבצע בשלבים נפרדים:
- התקשרות עם המכשיר: הקורא והארנק יוצרים קשר ראשוני באמצעות הצמדה ל-NFC (העברה סטטית או מוסכמת) או סריקה של קוד QR. במהלך השלב הזה, מתבצעת החלפה של מטא-נתונים של מעורבות המשתמש במכשיר ושל המפתח הציבורי האפמרי של הקורא (
EReaderKey). - חיבור להעברת נתונים: מתבצע משא ומתן על ערוץ מאובטח ומוצפן של Bluetooth Low Energy (BLE) (כשהקורא פועל במצב של לקוח מרכזי או שרת היקפי).
- בקשה מהמכשיר: הקורא מעביר
DeviceRequestעם קידוד CBOR שמציין את סוג המסמך המבוקש (למשלorg.iso.18013.5.1.mDL) ואת מרחבי השמות ורכיבי הנתונים הספציפיים המבוקשים. - הסכמת המשתמש ואימות המכשיר: מערכת Google Wallet מבקשת מהמשתמש לבדוק את רכיבי הנתונים המבוקשים ולאשר את השיתוף באמצעות אימות ביומטרי או נעילת מסך.
- תגובת המכשיר ואימות קריפטוגרפי: הארנק שולח בחזרה
DeviceResponseעם קידוד CBOR שמכיל את אובייקט האבטחה הנייד (MSO) החתום ואת רכיבי הנתונים החתומים על ידי המכשיר. הקורא מאמת את החתימות הקריפטוגרפיות מול אישורי בסיס מהימנים.
Multipaz Open-Source SDK
כדי להטמיע אפליקציה לקריאה או לאימות, מומלץ להשתמש ב-Multipaz, שהוא SDK של Kotlin Multiplatform (KMP) בקוד פתוח, שפותח במקור על ידי Google ונתרם ל-OpenWallet Foundation (OWF).
Multipaz מספקת הטמעה מוכנה לייצור של פרוטוקולי קריאה וארנק של ISO/IEC 18013-5, צינורות אימות קריפטוגרפיים, קידוד/פענוח של CBOR וסכימות של סוגי מסמכים שניתנות להרחבה.
שילוב של Multipaz באפליקציית הקורא
בשלבים הבאים מוסבר איך לשלב את Multipaz SDK באפליקציית קורא ל-Android.
שלב 1: מוסיפים תלויות
ספריות Multipaz מתפרסמות ב-Maven Central. מוסיפים את המודולים הנדרשים לקובץ build.gradle.kts של האפליקציה:
// build.gradle.kts
dependencies {
// Core Multipaz library (protocol engine, CBOR, crypto)
implementation("org.multipaz:multipaz:0.100.0")
// Android-specific platform bindings (NFC, BLE, Keystore)
implementation("org.multipaz:multipaz-android:0.100.0")
// Standardized document types (mDL, EU PID, etc.)
implementation("org.multipaz:multipaz-doctypes:0.100.0")
}
שלב 2: הגדרת הרשאות ל-Android
אימות פנים אל פנים דורש הרשאות חומרה להפעלת NFC, לסריקת מצלמה (להפעלת קוד QR) ולהעברת נתונים באמצעות Bluetooth עם צריכת אנרגיה נמוכה (BLE). מוסיפים את ההרשאות הבאות ל-AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- NFC Engagement -->
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
<!-- Camera for QR Code Engagement -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<!-- Bluetooth Low Energy Transport (Android 12+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- Legacy Bluetooth Permissions for Android 11 and lower -->
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
</manifest>
שלב 3: מאתחלים את מנוע האימות
אפשר להשתמש ב-VerificationHelper של Multipaz או בהפשטות של חיבור Reader כדי לטפל באירועי מעורבות ולנהל את מחזור החיים של תקשורת ה-BLE:
import org.multipaz.verification.VerificationHelper
import org.multipaz.cbor.Cbor
import org.multipaz.crypto.Crypto
class ReaderManager(private val context: android.content.Context) {
private var verificationHelper: VerificationHelper? = null
fun startListeningForEngagement() {
verificationHelper = VerificationHelper.Builder(
context = context,
listener = object : VerificationHelper.Listener {
override fun onDeviceConnected() {
// BLE channel established, send request
sendDeviceRequest()
}
override fun onResponseReceived(deviceResponseBytes: ByteArray) {
// Process and verify the received credential payload
handleDeviceResponse(deviceResponseBytes)
}
override fun onError(error: Throwable) {
// Handle transport or protocol errors
}
override fun onDeviceDisconnected(transportTransportSpecificTermination: Boolean) {
// Connection closed
}
}
).build()
}
}
שלב 4: בניית DeviceRequest
מציינים את סוג המסמך ואת רכיבי הנתונים האישיים שהאפליקציה צריכה לאמת. תמיד צריך להקפיד על העיקרון של חשיפה מינימלית (למשל, לבקש רק את age_over_21 ולא את birth_date המלא כשמאמתים גיל):
fun sendDeviceRequest() {
// Specify the DocType and requested elements
val docType = "org.iso.18013.5.1.mDL"
val namespace = "org.iso.18013.5.1"
// Map of requested elements: elementName -> intentToRetain
val requestedElements = mapOf(
"family_name" to false,
"given_name" to false,
"age_over_21" to false,
"portrait" to false,
"driving_privileges" to false
)
// Build ISO/IEC 18013-5 DeviceRequest structure
val deviceRequestBytes = verificationHelper?.buildDeviceRequest(
docType = docType,
itemsToRequest = mapOf(namespace to requestedElements)
)
if (deviceRequestBytes != null) {
verificationHelper?.sendDeviceRequest(deviceRequestBytes)
}
}
תמיכה בסוגי מסמכים נוספים (DocTypes)
Multipaz תומך בפרטי כניסה סטנדרטיים מחוץ לקופסה, ומספק ארכיטקטורה ניתנת להרחבה לבקשת סוגים של מסמכים מותאמים אישית או ספציפיים לדומיין.
1. סוגי מסמכים סטנדרטיים מובנים
הספרייה multipaz-doctypes מספקת מודלים מוגדרים מראש של סכימות לפרטי כניסה רגילים:
| מזהה סוג המסמך | רגיל / היקף | מרחב שמות אופייני | אלמנטים נפוצים |
|---|---|---|---|
org.iso.18013.5.1.mDL |
ISO/IEC 18013-5 רישיון נהיגה בנייד | org.iso.18013.5.1 |
family_name, given_name, birth_date, issue_date, expiry_date, issuing_authority, document_number, portrait, driving_privileges, age_over_18, age_over_21 |
eu.europa.ec.eudi.pid.1 |
נתוני זיהוי של אנשים בארנק הזהויות הדיגיטליות של האיחוד האירופי (EUDIW) | eu.europa.ec.eudi.pid.1 |
family_name, first_name, birth_date, nationality, issuing_country, issuing_authority, personal_administrative_number |
com.google.wallet.idcard.1 |
כרטיס תעודה מזהה ב-Google Wallet / מזהי בדיקה | com.google.wallet.idcard.1 |
given_name, family_name, birth_date, document_number, portrait |
2. שליחת בקשה לסוגי מסמכים בהתאמה אישית
כדי לבקש סוגי מסמכים בהתאמה אישית, צריך להגדיר את מחרוזת היעד docType ואת מיפויי מרחבי השמות התואמים כשיוצרים את DeviceRequest:
// Example: Requesting a custom event ticket credential
val customDocType = "com.example.events.ticket"
val customNamespace = "com.example.events.ticket.1"
val customRequestedItems = mapOf(
"ticket_id" to false,
"event_name" to false,
"seat_section" to false,
"vip_access" to false
)
val multiDocRequestBytes = verificationHelper?.buildMultiDocDeviceRequest(
documents = listOf(
DocumentRequest(
docType = customDocType,
namespaces = mapOf(customNamespace to customRequestedItems)
)
)
)
אימות קריפטוגרפי וניהול אמון
קבלת מטען ייעודי (payload) של תגובה היא רק השלב הראשון. הקוראים חייבים לבצע אימות קריפטוגרפי בן ארבעה שלבים כדי לאמת את האותנטיות והתקינות של פרטי האישור המוצגים.
| שלב האימות | מועד אחרון לאימות |
|---|---|
| 1. אימות המנפיק | אימות של IssuerAuth (COSE_Sign1) מול אישורי בסיס מהימנים של IACA |
| 2. בדיקת חלון תוקף | חשוב לוודא שמתקיים התנאי validFrom ≤ השעה הנוכחית ≤ validUntil |
| 3. בדיקת תקינות הנתונים | חישוב של תקצירי SHA-256 של הרכיבים שהוחזרו והצלבה שלהם עם MSO ValueDigests |
| 4. אימות מכשירים | אימות החתימה או ה-MAC של DeviceSigned באמצעות DeviceKey שמשויך ל-SessionTranscript |
1. צינור האימות בן 4 השלבים
- אימות המנפיק (
IssuerAuth):- האובייקט לאבטחת הנייד (MSO) נחתם על ידי רשות ההנפקה (
IssuerAuthמטען ייעודי). - הקורא מאמת את
COSE_Sign1החתימה באמצעות אישור החותם של המסמך, ומוודא ששרשרת האישורים מגיעה עד לאישור בסיסי של רשות מנפיקה (IACA) מהימנה.
- האובייקט לאבטחת הנייד (MSO) נחתם על ידי רשות ההנפקה (
- אימות חלון התוקף:
- הקורא בודק את חותמות הזמן
validityInfo.validFromו-validityInfo.validUntilב-MSO מול השעון הנוכחי של הקורא כדי לוודא שהאישורים לא פגו.
- הקורא בודק את חותמות הזמן
- אימות של יושרה נתונים (
ValueDigests):- עבור כל
IssuerSignedItemשמתקבל, הקורא מחשב את הגיבוב שלו (למשל SHA-256) ומוודא שהוא תואם לרשומת הגיבוב המתאימה במילוןValueDigestsשל ה-MSO.
- עבור כל
- אימות המכשיר (
DeviceSigned):- הקורא מוודא שהמכשיר שמציג את פרטי הכניסה מחזיק במפתח הפרטי שמתאים ל
DeviceKeyשפורסם בתוך ה-MSO החתום. - האימות מתבצע באמצעות אימות של
DeviceAuth(DeviceSignatureאוDeviceMac) דרךSessionTranscript, קישור הסשן למפתח האפמרי של הקורא ומניעת הפעלה חוזרת והתקפות אדם בתווך.
- הקורא מוודא שהמכשיר שמציג את פרטי הכניסה מחזיק במפתח הפרטי שמתאים ל
2. ניהול אישורי בסיס מהימנים של IACA
קוראים שמשמשים בסביבת ייצור חייבים לשמור על מאגר אישורים מהימן ומקומי שמכיל אישורי בסיס מהימנים של IACA:
- אישורי IACA של סביבת הייצור: מורידים ומגדירים אישורים בסיסיים מרשויות הנפקה רשמיות. אפשר לעיין ברשימה שלנו של מנפיקים נתמכים ואישורי IACA.
- AAMVA VICAL: במדינות בארה"ב, מערכות קוראים יכולות להשתלב עם שירות Verified Issuer Certificate Authority List (VICAL) של American Association of Motor Vehicle Administrators (AAMVA) כדי לסנכרן באופן אוטומטי את עוגני האמון של המדינה.
- שורשי בדיקה בארגז חול: כשבודקים מול פרטי כניסה לארגז חול, צריך לוודא שהקורא מהימן על ידי השורש של Google Sandbox IACA.
import org.multipaz.crypto.X509Cert
// Configure trusted IACA certificates in the trust store
val trustedCertificates = mutableListOf<X509Cert>()
// Add official state IACA certificates
trustedCertificates.add(X509Cert.fromPem(sampleStateIacaPem))
// Add Google Sandbox IACA root certificate for testing
trustedCertificates.add(X509Cert.fromPem(googleSandboxIacaPem))
val verifier = MultipazVerifier(trustStore = trustedCertificates)
val verificationResult = verifier.verify(deviceResponseBytes, sessionTranscript)
if (verificationResult.isIssuerAuthorized && verificationResult.isDeviceAuthenticated) {
// Credential is valid and authentic
} else {
// Reject presentation: cryptographic validation failed
}
אימות הקורא (מומלץ)
אימות הקורא מאפשר לאפליקציית הקורא להוכיח את הזהות שלה ל-Google Wallet באמצעות חתימה על מבנה ReaderAuthentication באמצעות אישור קורא מורשה מסוג X.509.
- למה מומלץ להשתמש באימות קורא: אימות קורא מאפשר לאפליקציה או למסוף הקורא להציג למשתמש זהות מהימנה. המאפיין הזה הוא אופציונלי כשמבקשים מאפיינים בסיסיים שגלויים לכולם (למשל, אימות של
age_over_21), אבל מומלץ מאוד להשתמש בו כשמשלבים את התכונה 'קורא' כדי להגביר את האמון של המשתמשים. יכול להיות שיהיה צורך להשתמש בו על פי חוק או מדיניות כשמבקשים מאפיינים רגישים (כמו מספר ביטוח לאומי מלא, כתובת מגורים או אישורים ספציפיים ממדינה). - איך זה עובד: הקורא כולל את שרשרת האישורים שלו וחותם על תמליל הסשן. במסך ההסכמה ב-Google Wallet מוצגים למשתמש הזהות המאומתת והשם של הארגון שקורא את הנתונים לפני שהנתונים נמסרים.
כלי בדיקה ופיתוח
כדי להאיץ את השילוב, אפשר להשתמש בכלי הפיתוח וביישומים לדוגמה הבאים:
- אפליקציות לדוגמה של Multipaz:
- משכפלים את מאגר Multipaz ומריצים את
IdentityReaderאפליקציית הדוגמה ל-Android כדי לבדוק את תהליכי האימות הפיזי.
- משכפלים את מאגר Multipaz ומריצים את
- כדי ליצור תעודת זהות לבדיקה ב-Google Wallet:
- כדי להקצות מסמך זיהוי סימולטיבי לבדיקה ב-Google Wallet באמצעות Utopia ePassport Simulator, פועלים לפי המדריך יצירת מסמך זיהוי לבדיקה ב-Google Wallet.
- בדיקה של כלי אימות מבוסס-אינטרנט:
- אפשר להשתמש בכתובת verifier.multipaz.org כדי לבדוק בקשות CBOR, לחקור שאילתות של תביעות בעלות ולבדוק מצגות מבוססות-אינטרנט של W3C / ISO 18013-7.
פתרון בעיות ואבחון בשטח
בטבלה הבאה מפורטות בעיות נפוצות שנתקלים בהן במהלך אימות אופליין, ומומלצים פתרונות לבעיות האלה:
| הבעיה / התסמין | שורש הבעיה | רזולוציה מומלצת |
|---|---|---|
| הזמן הקצוב לתפוגה של חיבור BLE הסתיים / החיבור נכשל |
|
|
| התקשורת בטאץ' באמצעות NFC נכשלת או נקטעת | המשתמש מרחיק את המכשיר הנייד מאנטנת הקורא לפני שהעברת הרשומה של העברת ה-BLE מסתיימת. |
|
UNTRUSTED_ISSUER / Certificate chain failure |
האישור של חותם המסמך לא מקושר לאף אישור מהימן של IACA במאגר המקומי של עוגני האמון בקורא. |
|
INVALID_VALIDITY_INFO / פג תוקף ה-MSO |
|
|
DEVICE_AUTHENTICATION_FAILED |
חוסר התאמה בין תמליל הסשן לבין הקורא והארנק, או חתימה לא חוקית של מכשיר זמני. |
|
| קריסה של הרשאות ב-Android 12 ואילך | האפליקציה ניסתה לסרוק או לפרסם באמצעות BLE ללא הרשאות זמן ריצה. |
|
הנחיות בנושא חוויית משתמש ופרטיות לקוראים פנים אל פנים
כשמעצבים קוראים פיזיים ואפליקציות נלוות:
- הקפדה על חשיפה סלקטיבית בממשק המשתמש: הצגת ההחלטה או המאפיין המינימלי הנדרש בלבד למפעיל (לדוגמה, הצגת סימן וי ירוק בולט והטקסט גיל 21 ומעלה, מאומת במקום הצגת תאריך הלידה המלא, הכתובת ומספר הרישיון של המשתמש).
- אינדיקטורים ברורים לאינטראקציה פיזית: צריך לסמן בבירור את אזור היעד של ה-NFC ולהציג רמזים חזותיים (כמו אנימציות או סרגלי התקדמות) שמראים כל שלב: הקשה / סריקה → התחברות → אימות → השלמה.
- טיפול בנתונים זמניים: אסור לאחסן או לרשום ביומן רכיבים של מידע אישי שמתקבלים מהארנק, אלא אם נדרש אחרת באופן מפורש על פי החוק החל, ובתנאי שמתבצע גילוי נאות באמצעות
intentToRetain = true.