אישור מאובטח של Android

כדי לעזור לכם לאשר את הצהרת המשתמשים של הכוונות כשהם יוזמים תהליך עסקה, למשל ביצוע תשלום, במכשירים נתמכים שבהם פועלת מערכת Android 9 (API רמה 28) ומעלה מאפשרים לך להשתמש ב'אישור הגנת Android'. כשמשתמשים בתהליך העבודה הזה, באפליקציה מוצגת בקשה למשתמש לאשר הצהרה קצרה שמאשרת מחדש את כוונתו להשלים את העסקה הרגישה.

אם המשתמש יאשר את ההצהרה, האפליקציה תוכל להשתמש במפתח מ-Android Keystore כדי לחתום על ההודעה שמוצגת בתיבת הדו-שיח. החתימה מצביעה, ברמת ודאות גבוהה מאוד, שהמשתמש ראה את ההצהרה ואישר אותה.

זהירות: התכונה 'אישור מוגן על ידי Android' לא מספקת ערוץ מידע מאובטח למשתמש. האפליקציה שלך לא יכולה להניח לאף אחד מובטחות לשמירה על סודיות, מעבר לאלה שפלטפורמת Android מציעה. לחשבון במיוחד, לא להשתמש בתהליך העבודה הזה כדי להציג מידע רגיש לא מוצגות בדרך כלל במכשיר של המשתמש.

לאחר שהמשתמש יאשר את ההודעה, תקינות ההודעה מובטחת, אבל האפליקציה עדיין צריכה להשתמש בהצפנה של נתונים בזמן ההעברה כדי להגן על הסודיות של ההודעה החתומה.

כדי לספק תמיכה באימות משתמשים ברמה גבוהה באפליקציה, מבצעים את השלבים הבאים:

  1. יצירת מפתח חתימה אסימטרי באמצעות הכיתה KeyGenParameterSpec.Builder. כשיוצרים את המפתח, צריך להעביר את true אל setUserConfirmationRequired(). בנוסף, קוראים לפונקציה setAttestationChallenge(), העברת ערך אתגר מתאים שסופק על ידי הצד הנסמך.

  2. רישום המפתח החדש שנוצר ואישור האימות של המפתח באמצעות על הצד הנסמך.

  3. לשלוח את פרטי העסקאות לשרת שלכם ולבקש ממנו ליצור ולהחזיר אובייקט גדול בינארי (BLOB) של נתונים נוספים. הנתונים הנוספים עשויים לכלול את הנתונים שצריך לאשר או טיפים לניתוח, כמו האזור של מחרוזת ההנחיה.

    להטמעה מאובטחת יותר, ה-BLOB חייב להכיל רכיב קריפטוגרפי צופן חד-פעמי (nonce) לצורך הגנה הפעלה מחדש של התקפות ולהבדיל בין עסקאות.

  4. הגדרת ConfirmationCallback שמיידע את האפליקציה כשהמשתמש מאשר את ההנחיה, תיבת דו-שיח לאישור:

    KotlinJava
    class MyConfirmationCallback : ConfirmationCallback() {
    
          override fun onConfirmed(dataThatWasConfirmed: ByteArray?) {
              super.onConfirmed(dataThatWasConfirmed)
              // Sign dataThatWasConfirmed using your generated signing key.
              // By completing this process, you generate a signed statement.
          }
    
          override fun onDismissed() {
              super.onDismissed()
              // Handle case where user declined the prompt in the
              // confirmation dialog.
          }
    
          override fun onCanceled() {
              super.onCanceled()
              // Handle case where your app closed the dialog before the user
              // responded to the prompt.
          }
    
          override fun onError(e: Exception?) {
              super.onError(e)
              // Handle the exception that the callback captured.
          }
      }
    public class MyConfirmationCallback extends ConfirmationCallback {
    
      @Override
      public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {
          super.onConfirmed(dataThatWasConfirmed);
          // Sign dataThatWasConfirmed using your generated signing key.
          // By completing this process, you generate a signed statement.
      }
    
      @Override
      public void onDismissed() {
          super.onDismissed();
          // Handle case where user declined the prompt in the
          // confirmation dialog.
      }
    
      @Override
      public void onCanceled() {
          super.onCanceled();
          // Handle case where your app closed the dialog before the user
          // responded to the prompt.
      }
    
      @Override
      public void onError(Throwable e) {
          super.onError(e);
          // Handle the exception that the callback captured.
      }
    }

    אם המשתמש יאשר את תיבת הדו-שיח, הקריאה החוזרת של onConfirmed() היא שנקראה. ה-BLOB של dataThatWasConfirmed הוא מבנה נתונים מסוג CBOR שמכיל, בין היתר, את הטקסט של ההנחיה שהמשתמש ראה ואת הנתונים הנוספים שהעברתם ל-builder של ConfirmationPrompt. להשתמש במפתח שנוצר בעבר כדי לחתום על dataThatWasConfirmed BLOB, לאחר מכן מעבירים את ה-BLOB הזה, יחד עם פרטי החתימה והעסקה, בחזרה לצד הנסמך.

    כדי לנצל את כל יתרונות האבטחה של אישור המוגן של Android, הצד הנסמך צריך לבצע את השלבים הבאים לאחר קבלת הודעה חתומה:

    1. בדיקת החתימה בהודעה וגם האימות (attestation) שרשרת האישורים של מפתח החתימה.
    2. בודקים שסימן TRUSTED_CONFIRMATION_REQUIRED מוגדר בתעודת האימות, כדי לציין שמפתח החתימה מחייב אישור של משתמש מהימן. אם מפתח החתימה הוא מפתח RSA, בודקים שהוא לא כולל PURPOSE_ENCRYPT או PURPOSE_DECRYPT לנכס.
    3. בודקים את extraData כדי לוודא שהודעת האישור הזו שייכת לבקשה חדשה שעדיין לא טופלה. השלב הזה מספק הגנה מפני בשידור חוזר של התקפות.
    4. לנתח את promptText כדי לקבל מידע על הפעולה שאושרה, או בקשה. חשוב לזכור ש-promptText הוא החלק היחיד בהודעה שהמשתמש אישר בפועל. הצד הנסמך לעולם לא צריך להניח שהוא הנתונים שאושרו שנכללים ב-extraData תואמים ל-promptText.
  5. מוסיפים לוגיקה דומה לזו שמוצגת בקטע הקוד הבא כדי להציג את תיבת הדו-שיח עצמה:

    KotlinJava
    // This data structure varies by app type. This is an example.
      data class ConfirmationPromptData(val sender: String,
              val receiver: String, val amount: String)
    
      val myExtraData: ByteArray = byteArrayOf()
      val myDialogData = ConfirmationPromptData("Ashlyn", "Jordan", "$500")
      val threadReceivingCallback = Executor { runnable -> runnable.run() }
      val callback = MyConfirmationCallback()
    
      val dialog = ConfirmationPrompt.Builder(context)
              .setPromptText("${myDialogData.sender}, send
                              ${myDialogData.amount} to
                              ${myDialogData.receiver}?")
              .setExtraData(myExtraData)
              .build()
      dialog.presentPrompt(threadReceivingCallback, callback)
      // This data structure varies by app type. This is an example.
      class ConfirmationPromptData {
          String sender, receiver, amount;
          ConfirmationPromptData(String sender, String receiver, String amount) {
              this.sender = sender;
              this.receiver = receiver;
              this.amount = amount;
          }
      };
      final int MY_EXTRA_DATA_LENGTH = 100;
      byte[] myExtraData = new byte[MY_EXTRA_DATA_LENGTH];
      ConfirmationPromptData myDialogData = new ConfirmationPromptData("Ashlyn", "Jordan", "$500");
      Executor threadReceivingCallback = Runnable::run;
      MyConfirmationCallback callback = new MyConfirmationCallback();
      ConfirmationPrompt dialog = (new ConfirmationPrompt.Builder(getApplicationContext()))
              .setPromptText("${myDialogData.sender}, send ${myDialogData.amount} to ${myDialogData.receiver}?")
              .setExtraData(myExtraData)
              .build();
      dialog.presentPrompt(threadReceivingCallback, callback);

מקורות מידע נוספים

למידע נוסף על אישור מוגן ב-Android, אפשר לעיין במקורות המידע הבאים.

בלוגים