במדריך הזה מוסבר איך גורמים מסתמכים (RP) ומפתחי קוראים יכולים להטמיע אימות פנים אל פנים (אופליין) של פרטי כניסה דיגיטליים שמוצגים מ-Google Wallet, בהתאם לתקן הבינלאומי ISO/IEC 18013-5.
אפשר לאמת באופן מאובטח את פרטי הכניסה הדיגיטליים ב-Google Wallet בסביבות פיזיות (כמו מסופים בנקודות מכירה, מקומות לאירועים, שערי כניסה לתחבורה ציבורית, קוראים של רשויות אכיפת החוק ואפליקציות של קוראים ניידים) בלי צורך בחיבור פעיל לאינטרנט בזמן ההצגה.
סקירה כללית על הצגה אופליין (ISO/IEC 18013-5)
תקן ISO/IEC 18013-5 מגדיר פרוטוקול סטנדרטי שמאפשר פעולה הדדית להצגה אופליין בין מחזיק (המכשיר הנייד של המשתמש שבו פועלת אפליקציית Google Wallet) לבין קורא / מאמת (מסוף פיזי או אפליקציה נלווית לנייד).
תהליך ההצגה מתרחש בשלבים נפרדים:
- אינטראקציה עם המכשיר: הקורא וה-Wallet יוצרים קשר ראשוני באמצעות הצמדה ב-NFC (העברה סטטית או העברה בהסכמה) או סריקת קוד QR. בשלב הזה, מתבצעת החלפה של המטא-נתונים של מעורבות המכשיר והמפתח הציבורי הזמני של הקורא (
EReaderKey). - חיבור להעברת נתונים: מתבצע משא ומתן על ערוץ Bluetooth Low Energy (BLE) מאובטח ומוצפן (כשהקורא פועל במצב Central Client או Peripheral Server).
- בקשת מכשיר: הקורא משדר
DeviceRequestבקידוד CBOR שמציין את סוג המסמך המבוקש (כמוorg.iso.18013.5.1.mDL) ואת מרחבי השמות ורכיבי הנתונים הספציפיים שהתבקשו. - הסכמת המשתמש ואימות המכשיר: אפליקציית Google Wallet מבקשת מהמשתמש לבדוק את רכיבי הנתונים המבוקשים ולאשר את השיתוף באמצעות אימות ביומטרי או נעילת מסך.
- תגובת המכשיר ואימות קריפטוגרפי: הארנק שולח בחזרה
DeviceResponseבקידוד CBOR שמכיל את אובייקט האבטחה לנייד (MSO) החתום ואת רכיבי הנתונים החתומים על ידי המכשיר. הקורא מאמת את החתימות הקריפטוגרפיות מול אישורי בסיס מהימנים.
Multipaz Open-Source SDK
כדי להטמיע אפליקציה לקריאה או לאימות, Google ממליצה להשתמש ב-Multipaz, ערכת SDK בקוד פתוח ל-Kotlin Multiplatform (KMP) שפותחה במקור על ידי Google ונתרמה ל-OpenWallet Foundation (OWF).
Multipaz מספקת הטמעה מוכנה לייצור של פרוטוקולים של קורא וארנק ISO/IEC 18013-5, צינורות עיבוד נתונים (pipeline) של אימות קריפטוגרפי, קידוד/פענוח של 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: הפעלת מנוע האימות
משתמשים בהפשטות של Multipaz לחיבור VerificationHelper או 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) חתום על ידי הרשות המנפיקה (מטען ייעודי (payload) של
IssuerAuth). - הקורא מאמת את החתימה של
COSE_Sign1באמצעות אישור החתימה על המסמך, ומוודא שהאישור מקושר לאישור בסיס מהימן של רשות האישורים המנפיקה (IACA).
- אובייקט האבטחה לנייד (MSO) חתום על ידי הרשות המנפיקה (מטען ייעודי (payload) של
- אימות חלון התוקף:
- הקורא בודק את חותמות הזמן
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 Root.
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 / כשל בשרשרת האישורים |
האישור של חותם המסמך לא מקושר לאף אישור IACA מהימן במאגר האישורים המקומי של הקורא. |
|
INVALID_VALIDITY_INFO / Expired MSO |
|
|
DEVICE_AUTHENTICATION_FAILED |
חוסר התאמה בתמליל הסשן בין הקורא לבין Wallet, או חתימת מכשיר זמנית לא תקינה. |
|
| קריסה שקשורה להרשאות ב-Android מגרסה 12 ואילך | האפליקציה ניסתה לסרוק או לפרסם באמצעות BLE בלי הרשאות בתחילת ההפעלה. |
|
הנחיות בנושא חוויית משתמש ופרטיות בקוראי כרטיסים פיזיים
כשמעצבים קוראים פיזיים ואפליקציות נלוות:
- השתמשו בחשיפה סלקטיבית בממשק המשתמש: הציגו למפעיל רק את ההחלטה או את המאפיין המינימלי הנדרש (לדוגמה, הציגו סימן וי ירוק בולט ו"גיל 21 ומעלה אומת" במקום להציג את תאריך הלידה המלא, הכתובת ומספר הרישיון של המשתמש).
- אינדיקטורים ברורים לאינטראקציה פיזית: סמנו בבירור את אזור היעד של ה-NFC והציגו רמזים חזותיים (כמו אנימציות או סרגלי התקדמות) שמראים כל שלב: הצמדה / סריקה ← מתחבר ← מאמת ← הושלם.
- טיפול בנתונים זמניים: אסור לאחסן או לתעד רכיבי מידע אישי שמתקבלים מהארנק, אלא אם הדבר נדרש במפורש על פי הדין החל וצוין ב-
intentToRetain = true.