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

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

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

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

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

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

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

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

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

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

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

    Kotlin

    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.
          }
      }

    Java

    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.
      }
    }

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

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

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

    Kotlin

    // 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)

    Java

      // 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 זמין במקורות המידע הבאים.

בלוגים