כדי שהמשתמשים יוכלו לאמת את הזהות שלהם באמצעות מפתחות גישה, האפליקציה צריכה קודם לרשום או ליצור את מפתח הגישה לחשבון שלהם.
כדי ליצור את מפתח הגישה, צריך לקבל משרת האפליקציה את הפרטים שנדרשים ליצירת מפתח הגישה, ואז לקרוא ל-API של Credential Manager, שמחזיר זוג מפתחות פרטיים וציבוריים. המפתח הפרטי שמוחזר מאוחסן בספק אישורים, כמו מנהל הסיסמאות של Google, כמפתח גישה. המפתח הציבורי מאוחסן בשרת האפליקציות.
דרישות מוקדמות
מוודאים שהגדרתם קישורי נכסים דיגיטליים ושהטירגוט הוא למכשירים עם Android 9 (רמת API 28) או גרסה מתקדמת יותר.
סקירה כללית
המדריך הזה מתמקד בשינויים שצריך לבצע באפליקציית הלקוח של הצד המסתמך כדי ליצור מפתח גישה, וכולל סקירה כללית קצרה של ההטמעה של שרת האפליקציה של הצד המסתמך. מידע נוסף על השילוב בצד השרת זמין במאמר בנושא רישום מפתחות גישה בצד השרת.
- מוסיפים יחסי תלות לאפליקציה: מוסיפים את הספריות הנדרשות של Credential Manager.
- יצירת מופע של Credential Manager: יוצרים מופע של Credential Manager.
- קבלת אפשרויות ליצירת אמצעי אימות משרת האפליקציה: משרת האפליקציה, שולחים לאפליקציית הלקוח את הפרטים שנדרשים ליצירת מפתח הגישה, כמו מידע על האפליקציה, על המשתמש, על
challengeועל שדות אחרים. - בקשת מפתח גישה: באפליקציה, משתמשים בפרטים שהתקבלו משרת האפליקציה כדי ליצור אובייקט
GetPublicKeyCredentialOption, ומשתמשים באובייקט הזה כדי להפעיל את ה-methodcredentialManager.getCredential()וליצור מפתח גישה. - טיפול בתגובה ליצירת מפתח הגישה: כשמקבלים את פרטי הכניסה באפליקציית הלקוח, צריך לקודד, לסדר אותם ואז לשלוח את המפתח הציבורי לשרת האפליקציה. בנוסף, צריך לטפל בכל החריגים שיכולים להתרחש במקרה של יצירת מפתח גישה.
- מאמתים ושומרים את המפתח הציבורי בשרת: מבצעים את השלבים בצד השרת כדי לאמת את מקור האישורים ואז שומרים את המפתח הציבורי.
- הודעה למשתמש: שליחת הודעה למשתמש על כך שמפתח הגישה שלו נוצר.
1. הוספת יחסי תלות לאפליקציה
מוסיפים את יחסי התלות הבאים לקובץ build.gradle של מודול האפליקציה:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.6.0-beta03") implementation("androidx.credentials:credentials-play-services-auth:1.6.0-beta03") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.6.0-beta03" implementation "androidx.credentials:credentials-play-services-auth:1.6.0-beta03" }
2. יצירת מופע של Credential Manager
משתמשים בהקשר של האפליקציה או הפעילות כדי ליצור אובייקט CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
3. קבלת אפשרויות ליצירת פרטי כניסה משרת האפליקציות
כשמשתמש לוחץ על הלחצן 'יצירת מפתח גישה' או כשמשתמש חדש נרשם, צריך לשלוח בקשה מהאפליקציה לשרת האפליקציה כדי לקבל את המידע שנדרש להתחלת תהליך ההרשמה של מפתח הגישה.
משתמשים בספרייה שתואמת ל-FIDO בשרת האפליקציה כדי לשלוח לאפליקציית הלקוח את המידע שנדרש ליצירת מפתח גישה, כמו מידע על המשתמש, על האפליקציה ומאפייני הגדרה נוספים. מידע נוסף זמין במאמר בנושא רישום מפתחות גישה בצד השרת.
באפליקציית הלקוח, מפענחים את אפשרויות היצירה של המפתח הציבורי שנשלחו על ידי שרת האפליקציה. הם בדרך כלל מיוצגים בפורמט JSON. מידע נוסף על פענוח קוד עבור לקוחות אינטרנט זמין במאמר קידוד ופענוח. באפליקציות לקוח ל-Android, צריך לטפל בפענוח בנפרד.
בקטע הקוד הבא מוצג המבנה של אפשרויות יצירת המפתח הציבורי שנשלחות משרת האפליקציה:
{
"challenge": "<base64url-encoded challenge>",
"rp": {
"name": "<relying party name>",
"id": "<relying party host name>"
},
"user": {
"id": "<base64url-encoded user ID>",
"name": "<user name>",
"displayName": "<user display name>"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"attestation": "none",
"excludeCredentials": [
{
"id": "<base64url-encoded credential ID to exclude>",
"type": "public-key"
}
],
"authenticatorSelection": {
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
השדות העיקריים באפשרויות ליצירת מפתח ציבורי כוללים:
challenge: מחרוזת אקראית שנוצרת על ידי השרת ומשמשת למניעת מתקפות שידור חוזר.-
rp: פרטים על האפליקציה.-
rp.name: שם האפליקציה. -
rp.id: הדומיין או תת-הדומיין של האפליקציה.
-
-
user: פרטים על המשתמש.-
id: המזהה הייחודי של המשתמש. הערך הזה לא יכול לכלול פרטים אישיים מזהים, למשל כתובות אימייל או שמות משתמש. אפשר להשתמש בערך אקראי של 16 בייט. -
name: מזהה ייחודי של החשבון שהמשתמש יזהה, כמו כתובת האימייל או שם המשתמש שלו. השם הזה יוצג בסלקטור החשבונות. אם משתמשים בשם משתמש, צריך להשתמש באותו ערך כמו באימות באמצעות סיסמה. -
displayName: שם אופציונלי וידידותי למשתמש של החשבון, שיוצג בבורר החשבונות.
-
authenticatorSelection: פרטים על המכשיר שישמש לאימות.-
authenticatorAttachment: מציין את אמצעי האימות המועדף. הערכים האפשריים הם: -platform: הערך הזה משמש לאמצעי אימות שמוטמע במכשיר של המשתמש, כמו חיישן טביעת אצבע. -cross-platform: הערך הזה משמש למכשירים ניידים כמו מפתחות אבטחה. בדרך כלל לא משתמשים בו בהקשר של מפתחות גישה. - לא צוין (מומלץ): אם לא מציינים את הערך הזה, המשתמשים יכולים ליצור מפתחות גישה במכשירים המועדפים עליהם. ברוב המקרים, האפשרות הטובה ביותר היא לא לציין את הפרמטר.-
requireResidentKey: כדי ליצור מפתח גישה, מגדירים את הערך של השדהBooleanהזה ל-true. -
residentKey: כדי ליצור מפתח גישה, מגדירים את הערך ל-required. -
userVerification: משמש לציון הדרישות לאימות המשתמש במהלך רישום מפתח גישה. הערכים האפשריים הם: –preferred: משתמשים בערך הזה אם חוויית המשתמש חשובה לכם יותר מההגנה, למשל בסביבות שבהן אימות המשתמש גורם ליותר חיכוך מאשר הגנה. -required: צריך להשתמש בערך הזה אם נדרשת הפעלה של שיטת אימות משתמש שזמינה במכשיר. -discouraged: משתמשים בערך הזה אם לא מומלץ להשתמש בשיטת אימות משתמש.
מידע נוסף עלuserVerificationזמין במאמר בנושא אימות משתמשים.
-
-
excludeCredentials: רשימת מזהי פרטי הכניסה במערך כדי למנוע יצירה של מפתח גישה כפול אם כבר קיים מפתח גישה עם אותו ספק פרטי כניסה.
4. בקשת מפתח גישה
אחרי שניתחתם את האפשרויות ליצירת מפתח ציבורי בצד השרת, יוצרים מפתח גישה על ידי הוספת האפשרויות האלה לאובייקט CreatePublicKeyCredentialRequest וקריאה ל-createCredential().
createPublicKeyCredentialRequest כולל את:
-
requestJson: האפשרויות ליצירת פרטי הכניסה שנשלחות על ידי שרת האפליקציה. -
preferImmediatelyAvailableCredentials: זהו שדה בוליאני אופציונלי שמגדיר אם להשתמש רק בפרטי כניסה שזמינים באופן מקומי או בפרטי כניסה שמסונכרנים עם ספק פרטי הכניסה כדי למלא את הבקשה, במקום בפרטי כניסה ממפתחות אבטחה או בתהליכי עבודה של מפתחות היברידיים. אלה השימושים האפשריים:-
false(ברירת מחדל): משתמשים בערך הזה אם הקריאה ל-Credential Manager הופעלה על ידי פעולת משתמש מפורשת. -
true: משתמשים בערך הזה אם מתבצעת קריאה ל-Credential Manager באופן אופורטוניסטי, למשל כשפותחים את האפליקציה בפעם הראשונה.
אם מגדירים את הערך ל-trueואין פרטי כניסה זמינים באופן מיידי, לא יוצג ממשק משתמש של Credential Manager והבקשה תיכשל באופן מיידי. במקרה כזה, יוחזר NoCredentialException לבקשות get ו-CreateCredentialNoCreateOptionExceptionלבקשות create.
-
-
origin: השדה הזה מוגדר אוטומטית באפליקציות ל-Android. לגבי דפדפנים ואפליקציות עם הרשאות דומות שצריך להגדיר בהן אתorigin, אפשר לעיין במאמר בנושא ביצוע קריאות ל-Credential Manager בשם צדדים אחרים באפליקציות עם הרשאות. -
isConditional: זהו שדה אופציונלי שערך ברירת המחדל שלו הואfalse. אם מגדירים את האפשרות הזו לערךtrue, ובמקרה שלמשתמש אין מפתח גישה, המערכת יוצרת בשמו מפתח גישה באופן אוטומטי בפעם הבאה שהוא נכנס לחשבון באמצעות סיסמה שמורה. מפתח הגישה מאוחסן אצל ספק פרטי הכניסה של המשתמש. כדי להשתמש בתכונה של יצירה מותנית, צריך את הגרסה האחרונה שלandroidx.credentials.
הפעלת הפונקציה createCredential() מציגה את ממשק המשתמש המובנה של מנהל האישורים, שמופיע כגיליון מלמטה ומבקש מהמשתמש להשתמש במפתח גישה, לבחור ספק אישורים וחשבון לאחסון. עם זאת, אם הערך של isConditional הוא true, ממשק המשתמש של הגיליון התחתון לא מוצג ומפתח הגישה נוצר באופן אוטומטי.
5. טיפול בתגובה
אחרי שהמשתמש מאומת באמצעות נעילת המסך של המכשיר, נוצר מפתח גישה והוא נשמר אצל ספק פרטי הכניסה שהמשתמש בחר.
התגובה אחרי קריאה מוצלחת של createCredential() היא אובייקט PublicKeyCredential.
האסימון PublicKeyCredential נראה כך:
{
"id": "<identifier>",
"type": "public-key",
"rawId": "<identifier>",
"response": {
"clientDataJSON": "<ArrayBuffer encoded object with the origin and signed challenge>",
"attestationObject": "<ArrayBuffer encoded object with the public key and other information.>"
},
"authenticatorAttachment": "platform"
}
באפליקציית הלקוח, מבצעים סריאליזציה של האובייקט ושולחים אותו לשרת האפליקציה.
מוסיפים קוד לטיפול בכשלים, כמו בקטע הקוד הבא:
fun handleFailure(e: CreateCredentialException) {
when (e) {
is CreatePublicKeyCredentialDomException -> {
// Handle the passkey DOM errors thrown according to the
// WebAuthn spec.
}
is CreateCredentialCancellationException -> {
// The user intentionally canceled the operation and chose not
// to register the credential.
}
is CreateCredentialInterruptedException -> {
// Retry-able error. Consider retrying the call.
}
is CreateCredentialProviderConfigurationException -> {
// Your app is missing the provider configuration dependency.
// Most likely, you're missing the
// "credentials-play-services-auth" module.
}
is CreateCredentialCustomException -> {
// You have encountered an error from a 3rd-party SDK. If you
// make the API call with a request object that's a subclass of
// CreateCustomCredentialRequest using a 3rd-party SDK, then you
// should check for any custom exception type constants within
// that SDK to match with e.type. Otherwise, drop or log the
// exception.
}
else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
}
}
6. אימות ושמירה של המפתח הציבורי בשרת האפליקציות
בשרת האפליקציות, צריך לאמת את פרטי הכניסה של המפתח הציבורי ואז לשמור את המפתח הציבורי.
כדי לאמת את המקור של פרטי הכניסה של המפתח הציבורי, משווים אותו לרשימת ההיתרים של אפליקציות שאושרו. אם למפתח יש מקור לא מזוהה, דוחים אותו.
כדי לקבל את טביעת האצבע מסוג SHA-256 של האפליקציה:
כדי להדפיס את אישור החתימה של אפליקציית הגרסה, מריצים את הפקודה הבאה בטרמינל:
keytool -list -keystore <path-to-apk-signing-keystore>בתגובה, צריך לזהות את טביעת האצבע מסוג SHA 256 של אישור החתימה, שמופיעה בתור
Certificate fingerprints block:SHA256.מקודדים את טביעת האצבע SHA256 באמצעות קידוד base64url. בדוגמה הבאה של Python מוסבר איך לקודד את טביעת האצבע בצורה נכונה:
import binascii import base64 fingerprint = '<SHA256 finerprint>' # your app's SHA256 fingerprint print(base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))מוסיפים
android:apk-key-hash: לתחילת הפלט מהשלב הקודם, כך שהפלט ייראה בערך כך:android:apk-key-hash:<encoded SHA 256 fingerprint>התוצאה צריכה להיות זהה למקור מותר בשרת האפליקציה. אם יש לכם כמה אישורי חתימה, כמו אישורים לניפוי באגים ולפרסום, או כמה אפליקציות, צריך לחזור על התהליך ולאשר את כל המקורות כחוקיים בשרת האפליקציה.
7. הודעה למשתמש
אחרי שמפתח הגישה נוצר בהצלחה, צריך להודיע למשתמשים על מפתח הגישה ולעדכן אותם שהם יכולים לנהל את מפתחות הגישה שלהם דרך אפליקציית ספק האישורים או בהגדרות האפליקציה. הודעה למשתמשים באמצעות תיבת דו-שיח, התראה או חלונית מידע בהתאמה אישית. אם ישות זדונית יוצרת מפתח גישה באופן לא צפוי, צריך לשלוח התראה אבטחה מיידית. לכן, מומלץ להוסיף לשיטות האלה באפליקציה גם תקשורת חיצונית, כמו אימייל.
שיפור חוויית המשתמש
כדי לשפר את חוויית המשתמש כשמטמיעים הרשמה באמצעות Credential Manager, מומלץ להוסיף פונקציונליות לשחזור פרטי הכניסה ולהשבית את תיבות הדו-שיח של המילוי האוטומטי.
הוספת פונקציונליות לשחזור פרטי הכניסה במכשיר חדש
כדי לאפשר למשתמשים להיכנס לחשבונות שלהם במכשיר חדש בצורה חלקה, צריך להטמיע את הפונקציונליות של שחזור פרטי הכניסה. הוספת פרטי כניסה לשחזור עם BackupAgent מאפשרת למשתמשים להיכנס לחשבון כשהם פותחים את האפליקציה המשוחזרת במכשיר חדש, כך שהם יכולים להשתמש באפליקציה באופן מיידי.
ביטול המילוי האוטומטי בשדות של פרטי הכניסה (אופציונלי)
במסכי אפליקציות שבהם המשתמשים אמורים להשתמש בממשק המשתמש של דף התחתון של מנהל פרטי הכניסה לאימות, מוסיפים את המאפיין isCredential לשדות של שם המשתמש והסיסמה. ההגדרה הזו מונעת חפיפה בין תיבות הדו-שיח של המילוי האוטומטי (FillDialog ו-SaveDialog) לבין ממשק המשתמש של הגיליון התחתון של Credential Manager.
מאפיין isCredential נתמך ב-Android מגרסה 14 ואילך.
בדוגמה הבאה אפשר לראות איך מוסיפים את המאפיין isCredential לשדות הרלוונטיים של שם המשתמש והסיסמה בתצוגות הרלוונטיות באפליקציה:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isCredential="true" />