סקירה כללית על אמולציית כרטיסים מבוססת-מארח

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

ב-Android מגרסה 4.4 ואילך יש שיטה נוספת לאמולציית כרטיסים שלא כוללת רכיב מאובטח, שנקראת אמולציית כרטיס מבוססת-מארח. כך כל אפליקציה ל-Android יכולה לדמות כרטיס ולדבר ישירות לקורא ה-NFC. כאן מוסבר איך פועלת אמולציה של כרטיסים מבוססי מארח (HCE) ב-Android, ואיך אפשר לפתח אפליקציה שמבצעת אמולציה של כרטיס NFC באמצעות הטכניקה הזו.

אמולציית כרטיסים עם רכיב מאובטח

כשהכלי להדמיית כרטיס NFC מסופק באמצעות רכיב מאובטח, הכרטיס שרוצים לדמות מוקצה לרכיב המאובטח במכשיר באמצעות אפליקציה ל-Android. לאחר מכן, כשהמשתמש מחזיק את המכשיר מעל מסוף NFC, בקר ה-NFC במכשיר מפנה את כל הנתונים מהקורא ישירות לרכיב המאובטח. איור 1 ממחיש את הקונספט הזה:

תרשים עם קורא NFC שעובר דרך בקר NFC כדי לאחזר מידע מרכיב מאובטח
איור 1. אמולציית כרטיס NFC עם רכיב מאובטח.

הרכיב המאובטח עצמו מבצע את התקשורת עם מסוף ה-NFC, ואף אפליקציה ל-Android לא מעורבת בעסקה. אחרי השלמת העסקה, אפליקציה ל-Android יכולה לשלוח שאילתות ישירות לרכיב המאובטח כדי לבדוק את סטטוס העסקה ולהודיע למשתמש.

אמולציית כרטיסים מבוססת-מארח

כשכרטיס NFC מומר באמצעות אמולציה של כרטיס מבוססת-מארח, הנתונים מנותבים ישירות ל-CPU של המארח במקום לעבור לרכיב מאובטח. איור 2 ממחיש איך פועלת אמולציה של כרטיסים מבוססי מארח:

תרשים עם קורא NFC שעובר דרך בקר NFC כדי לאחזר מידע מה-CPU
איור 2. הדמיה של כרטיס NFC ללא רכיב מאובטח.

כרטיסי NFC ופרוטוקולים נתמכים

תרשים שמציג סטאק פרוטוקול HCE
איור 3. סטאק הפרוטוקולים של HCE ב-Android.

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

מערכת Android מגרסה 4.4 ואילך תומכת במספר פרוטוקולים שנפוצים בשוק כיום. כרטיסים רבים שכבר קיימים מבוססים על הפרוטוקולים האלה, כמו כרטיסי תשלום ללא מגע. הפרוטוקולים האלה נתמכים גם על ידי הרבה קוראי NFC בשוק כיום, כולל מכשירי Android NFC שפועלים כקוראים בעצמם (ראו את הכיתה IsoDep). כך תוכלו ליצור ולפרוס פתרון NFC מקצה לקצה שמבוסס על HCE, ולהשתמש רק במכשירים עם Android.

באופן ספציפי, מערכת Android 4.4 ואילך תומכת בכרטיסים באמצעות אמולציה של כרטיסים שמבוססים על מפרט ISO/IEC 14443-4 של NFC-Forum (על סמך ISO/IEC 14443-4) ומעבדים של Application Protocol Data Data (APDU) – כפי שמוגדר במפרט ISO/IEC 7816-4. מערכת Android מחייבת הדמיה של ISO-DEP רק מעל הטכנולוגיה Nfc-A‏ (ISO/IEC 14443-3 Type A). התמיכה בטכנולוגיית Nfc-B (ISO/IEC 14443-4 Type B) היא אופציונלית. איור 3 ממחיש את השכבות של כל המפרטים האלה.

שירותי HCE

ארכיטקטורת HCE ב-Android מבוססת על רכיבי Android Service (שנקראים שירותי HCE). אחד היתרונות העיקריים של שירות הוא שהוא יכול לפעול ברקע בלי ממשק משתמש. זוהי התאמה טבעית לאפליקציות HCE רבות, כמו כרטיסי מועדון לקוחות או כרטיסי תחבורה ציבורית, שבהם המשתמש לא צריך להפעיל אפליקציה כדי להשתמש בהם. במקום זאת, מקישים על המכשיר מול קורא ה-NFC כדי להפעיל את השירות הנכון, אם הוא לא פועל כבר, ולבצע את העסקה ברקע. כמובן, אתם יכולים להפעיל ממשקי משתמש נוספים (כמו התראות למשתמשים) מהשירות שלכם במקרים המתאימים.

בחירת שירות

כשהמשתמש מכה במכשיר על קורא NFC, מערכת Android צריכה לדעת איזה שירות HCE קורא ה-NFC רוצה לתקשר איתו. מפרט ISO/IEC 7816-4 מגדיר דרך לבחירת אפליקציות, סביב מזהה אפליקציה (AID). מזהה AID מורכב מ-16 בייטים לכל היותר. אם אתם מבצעים הדמיה של כרטיסים בתשתית קיימת של קורא NFC, מזהי ה-AID שהקוראים האלה מחפשים הם בדרך כלל מזהי AID ידועים ורשומים באופן ציבורי (לדוגמה, מזהי ה-AID של רשתות תשלומים כמו Visa ו-MasterCard).

אם אתם רוצים לפרוס תשתית קוראים חדשה לאפליקציה שלכם, תצטרכו לרשום מזהי AID משלכם. תהליך הרישום של מזהי AID מוגדר במפרט ISO/IEC 7816-5. אם אתם פורסים אפליקציית HCE ל-Android, מומלץ לרשום מזהה AID בהתאם ל-7816-5 כדי למנוע התנגשויות עם אפליקציות אחרות.

קבוצות AID

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

רשימה של מזהי AID שנשמרים יחד נקראת קבוצת AID. לכל מזהי ה-AID בקבוצת AID, מערכת Android מבטיחה את אחת מהאפשרויות הבאות:

  • כל מזהי ה-AID בקבוצה מנותבים לשירות ה-HCE הזה.
  • אף מזהה AID בקבוצה לא ינותב לשירות HCE הזה (לדוגמה, כי המשתמש העדיף שירות אחר שביקש גם מזהה AID אחד או יותר בקבוצה שלכם).

במילים אחרות, אין מצב ביניים שבו חלק מ-AIDs בקבוצה יכולים לעבור לשירות HCE אחד וחלק לשירות אחר.

קבוצות וקטגוריות של סיוע בינלאומי

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

במכשירי Android מגרסה 4.4 ואילך יש תמיכה בשתי קטגוריות:

הטמעת שירות HCE

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

בדיקת התמיכה ב-HCE

האפליקציה יכולה לבדוק אם המכשיר תומך ב-HCE על ידי חיפוש התכונה FEATURE_NFC_HOST_CARD_EMULATION. משתמשים בתג <uses-feature> במניפסט של האפליקציה כדי להצהיר שהאפליקציה משתמשת בתכונה HCE, ולהצהיר אם היא נדרשת לצורך הפעלת האפליקציה או לא.

הטמעת שירותים

ב-Android מגרסה 4.4 ואילך יש מחלקה נוחה בשם Service שאפשר להשתמש בה כבסיס להטמעת שירות HCE: המחלקה HostApduService.

השלב הראשון הוא להרחיב את HostApduService, כפי שמוצג בדוגמת הקוד הבאה:

Kotlin

class MyHostApduService : HostApduService() {

    override fun processCommandApdu(commandApdu: ByteArray, extras: Bundle?): ByteArray {
       ...
    }

    override fun onDeactivated(reason: Int) {
       ...
    }
}

Java

public class MyHostApduService extends HostApduService {
    @Override
    public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
       ...
    }
    @Override
    public void onDeactivated(int reason) {
       ...
    }
}

HostApduService מכריז על שתי שיטות מופשטות שצריך לבטל את הגדרת ברירת המחדל שלהן ולהטמיע אותן. אחד מהם, processCommandApdu(), נקרא בכל פעם שקורא NFC שולח לשירות יחידת נתונים של פרוטוקול אפליקציה (APDU). הודעות APDU מוגדרות במפרט ISO/IEC 7816-4. חבילות APDU הן החבילות ברמת האפליקציה שמתחלפות בין קורא ה-NFC לבין שירות ה-HCE. הפרוטוקול ברמת האפליקציה הוא חצי דופלקס: קורא ה-NFC שולח לכם פקודה APDU וממתין לכך שתשיבו לו תשובה APDU.

כפי שצוין קודם, Android משתמש ב-AID כדי לקבוע איזה שירות HCE הקורא רוצה ליצור איתו קשר. בדרך כלל, ה-APDU הראשון שקורא ה-NFC שולח למכשיר הוא SELECT AID APDU. ה-APDU הזה מכיל את ה-AID שקורא ה-NFC רוצה לתקשר איתו. מערכת Android מחלצת את ה-AID הזה מה-APDU, פותרת אותו לשירות HCE ומעבירה את ה-APDU לשירות הזה.

כדי לשלוח APDU תגובה, מחזירים את הבייטים של APDU התגובה מ-processCommandApdu(). שימו לב שה-method הזה נקרא ב-thread הראשי של האפליקציה, שאותו אסור לחסום. אם אי אפשר לחשב ולשלוח תשובה APDU באופן מיידי, צריך להחזיר null. לאחר מכן תוכלו לבצע את העבודה הנדרשת בשרשור אחר ולהשתמש ב-method‏ sendResponseApdu() שמוגדר בכיתה HostApduService כדי לשלוח את התשובה בסיום.

מערכת Android ממשיכה להעביר מפתחות APDU חדשים מהקורא לשירות שלכם, עד שמתקיים אחד מהתנאים הבאים:

  • קורא ה-NFC שולח עוד APDU של SELECT AID, ומערכת ההפעלה מעבירה אותו לשירות אחר.
  • קישור ה-NFC בין קורא ה-NFC לבין המכשיר לא תקין.

בשני המקרים האלה, ההפעלה של ההטמעה של onDeactivated() בכיתה תתבצע עם ארגומנט שמציין איזה מהשניים קרה.

אם אתם עובדים עם תשתית קיימת של קורא, עליכם להטמיע את הפרוטוקול הקיים ברמת האפליקציה שהקוראים מצפים לו בשירות ה-HCE.

אם אתם פורסים תשתית חדשה של קורא שבשליטתכם, תוכלו להגדיר את הפרוטוקול ואת רצף ה-APDU שלכם. כדאי להגביל את כמות ה-APDU ואת גודל הנתונים להחלפה: כך המשתמשים יצטרכו להחזיק את המכשיר מעל קורא ה-NFC רק למשך זמן קצר. גבול עליון סביר הוא כ-1KB של נתונים, שבדרך כלל אפשר להעביר תוך 300ms.

הצהרה על מניפסט השירות ורישום AID

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

  1. כדי לומר לפלטפורמה שמדובר בשירות HCE שמטמיע ממשק HostApduService, צריך להוסיף להצהרת השירות מסנן Intent עבור הפעולה SERVICE_INTERFACE.

  2. כדי להודיע לפלטפורמה אילו קבוצות של AIDs השירות הזה מבקש, צריך לכלול את התג SERVICE_META_DATA <meta-data> בהצהרה על השירות, כך שיצביע על משאב XML עם מידע נוסף על שירות ה-HCE.

  3. מגדירים את המאפיין android:exported לערך true, ומחייבים את ההרשאה android.permission.BIND_NFC_SERVICE בהצהרת השירות. האפשרות הראשונה מבטיחה שאפשר יהיה לאגד את השירות לאפליקציות חיצוניות. אחר כך אוכפים שרק אפליקציות חיצוניות עם ההרשאה android.permission.BIND_NFC_SERVICE יכולות לקשר לשירות. מכיוון ש-android.permission.BIND_NFC_SERVICE היא הרשאת מערכת, כך אפשר לאכוף ביעילות שרק מערכת Android תוכל לקשר את השירות.

דוגמה להצהרת מניפסט HostApduService:

<service android:name=".MyHostApduService" android:exported="true"
         android:permission="android.permission.BIND_NFC_SERVICE">
    <intent-filter>
        <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
    </intent-filter>
    <meta-data android:name="android.nfc.cardemulation.host_apdu_service"
               android:resource="@xml/apduservice"/>
</service>

תג המטא-נתונים הזה מפנה לקובץ apduservice.xml. לפניכם דוגמה לקובץ כזה עם הצהרה אחת על קבוצת AID שמכילה שני AIDs קנייניים:

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
           android:description="@string/servicedesc"
           android:requireDeviceUnlock="false">
    <aid-group android:description="@string/aiddescription"
               android:category="other">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</host-apdu-service>

התג <host-apdu-service> חייב להכיל מאפיין <android:description> שמכיל תיאור ידידותי למשתמש של השירות שאפשר להציג בממשק המשתמש של האפליקציה. אפשר להשתמש במאפיין requireDeviceUnlock כדי לציין שהמכשיר נעול לפני שמפעילים את השירות הזה לטיפול ב-APDU.

התווית <host-apdu-service> חייבת להכיל תג <aid-group> אחד לפחות. כל תג <aid-group> חייב לבצע את הפעולות הבאות:

  • מכילה מאפיין android:description שמכיל תיאור ידידותי למשתמש של קבוצת ה-AID, שמתאים להצגה בממשק המשתמש.
  • מגדירים את המאפיין android:category כך שיציין את הקטגוריה שאליה משתייכת קבוצת ה-AID, למשל קבועי המחרוזות שמוגדרים על ידי CATEGORY_PAYMENT או CATEGORY_OTHER.
  • מכילים תג <aid-filter> אחד או יותר, שכל אחד מהם מכיל מזהה AID יחיד. מציינים את ה-AID בפורמט הקסדצימלי ומוודאים שהוא מכיל מספר תווים אי זוגי.

בנוסף, לאפליקציה צריכה להיות ההרשאה NFC כדי להירשם כשירות HCE.

פתרון סכסוכים ב-AID

במכשיר אחד אפשר להתקין כמה רכיבי HostApduService, ואפשר לרשום את אותו AID ביותר משירות אחד. מערכת Android קובעת איזה שירות להפעיל לפי השלבים הבאים:

  1. אם אפליקציית הארנק שמוגדרת כברירת המחדל על ידי המשתמש רשמה את ה-AID, האפליקציה תופעל.
  2. אם אפליקציית הארנק שמוגדרת כברירת מחדל לא רשמה את ה-AID, מופעל השירות שבו נרשם ה-AID.
  3. אם יותר משירות אחד רשם את ה-AID, מערכת Android תשאל את המשתמש איזה שירות להפעיל.

איך בודקים אם האפליקציה מוגדרת כברירת מחדל לאחסון הכרטיסים

אפליקציות יכולות לבדוק אם הן מוגדרות כברירת מחדל לאפליקציית הארנק על ידי העברת הערך RoleManager.ROLE_WALLET אל RoleManager.isRoleHeld().

אם האפליקציה שלכם לא מוגדרת כברירת המחדל, תוכלו להעביר את RoleManager.ROLE_WALLET ל-RoleManager.createRequestRoleIntent() כדי לבקש את התפקיד שמוגדר כברירת מחדל ב-Wallet.

אפליקציות Wallet

מערכת Android מתייחסת לשירותי HCE שהצהירו על קבוצת AID עם קטגוריית התשלום בתור אפליקציות ארנק. ב-Android מגרסה 15 ואילך יש תפקיד ברירת מחדל לאפליקציית ארנק, והמשתמשים יכולים לבחור אותו דרך הגדרות > אפליקציות > אפליקציות ברירת מחדל. ההגדרה הזו קובעת איזו אפליקציית Wallet מופעלת כברירת מחדל כשמצמידים את המכשיר למסוף תשלומים.

נכסים נדרשים לאפליקציות Wallet

כדי לספק חוויית משתמש חזותית מושכת יותר, אפליקציות ארנק HCE נדרשות לספק באנר שירות.

‫Android מגרסה 13 ואילך

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

Android מגרסה 12 ומטה

מגדירים את הגודל של באנר השירות ל-260x96dp, ואז מגדירים את הגודל של באנר השירות בקובץ ה-XML של המטא-נתונים. לשם כך, מוסיפים את המאפיין android:apduServiceBanner לתג <host-apdu-service>, שמפנה למשאב שניתן לשרטוט. דוגמה:

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
        android:description="@string/servicedesc"
        android:requireDeviceUnlock="false"
        android:apduServiceBanner="@drawable/my_banner">
    <aid-group android:description="@string/aiddescription"
               android:category="payment">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</host-apdu-service>

מצב צפייה

ב-Android 15 הוספנו את התכונה מצב ניראות. כשהאפשרות מופעלת, מצב התצפית מאפשר למכשיר לעקוב אחרי לולאות הסקרים של NFC ולשלוח התראות עליהן לרכיבי HostApduService המתאימים, כדי שיוכלו להתכונן לאינטראקציה עם מסוף NFC נתון. HostApduService יכול להעביר את המכשיר למצב תצפית על ידי העברת true אל setObserveModeEnabled(). ההגדרה הזו מורה ל-NFC stack לא לאפשר עסקאות NFC, ובמקום זאת לעקוב באופן פסיבי אחרי לולאות הסקרים.

מסננים של לולאת הסקרים

אפשר לרשום מסננים של לולאת סקרים ל-HostApduService באמצעות אחת מהשיטות הבאות:

כשמסנן לולאת דגימה תואם למסגרות לא סטנדרטיות של סקרים, סטאק ה-NFC מנתב את המסגרות של הסקרים ל-HostApduService המתאים על ידי קריאה לשיטה processPollingFrames(). כך השירות יכול לבצע את כל הפעולות הנדרשות כדי לוודא שהמשתמש מוכן לבצע עסקה ויש לו כוונה לעשות זאת – למשל, אימות המשתמש. אם קורא ה-NFC משתמש רק בפריימים רגילים בלולאת הסקרים שלו, סטאק ה-NFC מנווט את פריימים הסקרים האלה לשירות המועדף בחזית, אם השירות הזה נמצא בחזית, או לבעלים של תפקיד הארנק שמוגדר כברירת מחדל, במקרה אחר.

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

להגיב ללולאות בקלפי ולצאת ממצב 'תצפית'

כשהשירות מוכן לבצע עסקה, הוא יכול לצאת ממצב התצפית על ידי העברת הערך false אל setObserveModeEnabled(). לאחר מכן, מקבץ ה-NFC יאפשר להמשיך בעסקאות.

רכיבי HostApduService יכולים לציין שצריך להפעיל את מצב 'צפייה' בכל פעם שהם שירות התשלומים המועדף. לשם כך, צריך להגדיר את shouldDefaultToObserveMode לערך true במניפסט או בקריאה ל-CardEmulation.setShouldDefaultToObserveModeForService().

אפשר גם להשתמש ברכיבים HostApduService ו-OffHostApduService כדי לציין שמסננים של לולאות סקירה (polling) שתואמים למסגרות של לולאות סקירה שהתקבלו צריכים להשבית באופן אוטומטי את מצב התצפית ולאפשר לעסקאות להמשיך, על ידי הגדרת autoTransact לערך true בהצהרה PollingLoopFilter במניפסט.

התנהגות המסך כשהוא כבוי וכשהמסך נעול

ההתנהגות של שירותי HCE משתנה בהתאם לגרסת Android שפועלת במכשיר.

Android מגרסה 12 ואילך

באפליקציות שמטרגטות את Android מגרסה 12 ואילך (רמת API 31 ואילך), אפשר להפעיל תשלומים ב-NFC בלי שהמסך של המכשיר יהיה פעיל. לשם כך, מגדירים את הערך של requireDeviceScreenOn לערך false.

Android מגרסה 10 ואילך

במכשירים עם Android מגרסה 10 (רמת API‏ 29) ואילך יש תמיכה ב-NFC מאובטח. כשה-NFC המאובטח מופעל, כל אמולרטורים של כרטיסים (אפליקציות מארח ואפליקציות מחוץ למארח) לא זמינים כשמסך המכשיר כבוי. כשה-NFC המאובטח מושבת, אפליקציות מחוץ למארח זמינות כשמסך המכשיר כבוי. אפשר לבדוק אם יש תמיכה ב-NFC מאובטח באמצעות isSecureNfcSupported().

במכשירים עם Android מגרסה 10 ואילך, אותה פונקציונליות של הגדרת android:requireDeviceUnlock ל-true חלה גם על מכשירי Android מגרסה 9 ומטה, אבל רק כש-Secure NFC מושבת. כלומר, אם Secure NFC מופעל, שירותי HCE לא יכולים לפעול מסך הנעילה, ללא קשר להגדרה של android:requireDeviceUnlock.

Android מגרסה 9 ומטה

במכשירים עם Android 9 (רמת API 28) ומטה, בקר ה-NFC ומעבד האפליקציות מושבתים לחלוטין כשהמסך של המכשיר כבוי. לכן, שירותי HCE לא פועלים כשהמסך כבוי.

בנוסף, ב-Android 9 ומטה, שירותי HCE יכולים לפעול ממסך הנעילה. עם זאת, האפשרות הזו נשלטת על ידי המאפיין android:requireDeviceUnlock בתג <host-apdu-service> של שירות ה-HCE. כברירת מחדל, לא נדרש לבטל את נעילת המכשיר, והשירות מופעל גם אם המכשיר נעול.

אם מגדירים את המאפיין android:requireDeviceUnlock לערך true בשירות ה-HCE, מערכת Android מבקשת מהמשתמש לבטל את נעילת המכשיר במקרים הבאים:

  • המשתמש מכה על קורא NFC.
  • קורא ה-NFC בוחר AID שטופל לגבי השירות שלכם.

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

יכולת לדור בכפיפה עם כרטיסים עם רכיב מאובטח

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

הדו-קיום הזה מבוסס על עיקרון שנקרא ניתוב AID. בקר ה-NFC שומר טבלת ניתוב שמכילה רשימה (סופית) של כללי ניתוב. כל כלל ניתוב מכיל מזהה AID ויעד. היעד יכול להיות מעבד המארח שבו פועלות אפליקציות Android, או רכיב מאובטח מחובר.

כשקורא NFC שולח APDU עם SELECT AID, בקר ה-NFC מנתח אותו ובודק אם מזהי ה-AID תואמים ל-AID כלשהו בטבלת הניתוב. אם הוא תואם, ה-APDU הזה וכל ה-APDUs הבאים אחריו נשלחים ליעד שמשויך ל-AID, עד שמתקבל APDU נוסף של SELECT AID או שהקישור ל-NFC נקטע.

איור 4 ממחיש את הארכיטקטורה הזו:

תרשים שבו קורא NFC מתקשר גם עם רכיב מאובטח וגם עם המעבד
איור 4. מכשירי Android שפועלים עם רכיב מאובטח ועם אמולציה של כרטיס מארח.

בדרך כלל, בקר ה-NFC מכיל גם מסלול ברירת מחדל ל-APDU. אם מזהה ה-AID לא נמצא בטבלת הניתוב, המערכת משתמשת במסלול ברירת המחדל. למרות שההגדרה הזו עשויה להיות שונה ממכשיר למכשיר, מכשירי Android נדרשים כדי לוודא שמזהי ה-AID שרושמים באפליקציה ינותבו כראוי למארח.

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

בקטע הבא מוסבר איך להצהיר על מזהי AID לאפליקציות שמשתמשות ברכיב מאובטח להדמיית כרטיס.

רישום AID של רכיב מאובטח

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

  • הפעולה שבה נעשה שימוש במסנן Intent חייבת להיות מוגדרת כ-SERVICE_INTERFACE.
  • מאפיין השם של המטא-נתונים צריך להיות SERVICE_META_DATA.
  • קובץ ה-XML של המטא-נתונים חייב להשתמש בתג הבסיס (root) <offhost-apdu-service>.

    <service android:name=".MyOffHostApduService" android:exported="true"
           android:permission="android.permission.BIND_NFC_SERVICE">
      <intent-filter>
          <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
      </intent-filter>
      <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service"
                 android:resource="@xml/apduservice"/>
    </service>

דוגמה לקובץ apduservice.xml התואם שמירשם שני מזהי AID:

<offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
           android:description="@string/servicedesc">
    <aid-group android:description="@string/subscription" android:category="other">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</offhost-apdu-service>

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

המאפיין android:apduServiceBanner נדרש לשירותים מחוץ למארח שהם אפליקציות תשלומים, וכדי שאפשר יהיה לבחור אותם כאפליקציית תשלומים שמוגדרת כברירת מחדל.

קריאה לשירות מחוץ למארח

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

HCE ואבטחה

ארכיטקטורת HCE מספקת רכיב אבטחה מרכזי אחד: השירות מוגן על ידי הרשאת המערכת BIND_NFC_SERVICE, ולכן רק מערכת ההפעלה יכולה לקשר את השירות ולתקשר איתו. כך אפשר לוודא שכל APDU שאתם מקבלים הוא למעשה APDU שהמערכת הפעלה קיבלה מבקר ה-NFC, ושכל APDU שאתם שולחים בחזרה עובר רק למערכת ההפעלה, שמעבירה את ה-APDU ישירות לבקר ה-NFC.

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

כדי לאחסן ולשלוף באופן מאובטח את הנתונים שרוצים לשלוח משירות ה-HCE, אפשר להשתמש, למשל, ב-Android Application Sandbox, שמבודד את נתוני האפליקציה מאפליקציות אחרות. למידע נוסף על אבטחה ב-Android, קראו את המאמר טיפים בנושא אבטחה.

פרטים ופרמטרים של פרוטוקול

הקטע הזה מיועד למפתחים שרוצים להבין באילו פרמטרים של פרוטוקול משתמשים במכשירי HCE במהלך שלבי המניעה מהתנגשות וההפעלה של פרוטוקולי ה-NFC. כך אפשר ליצור תשתית של קורא שתואמת למכשירי Android HCE.

פרוטוקול Nfc-A (ISO/IEC 14443 type A) למניעת התנגשויות והפעלה

כחלק מהפעלת פרוטוקול Nfc-A, מתבצעת החלפה של כמה פריימים.

בחלק הראשון של ההחלפה, מכשיר ה-HCE מציג את ה-UID שלו. צריך להניח שלמכשירי HCE יש UID אקראי. כלומר, בכל הקשה, ה-UID שמוצג לקורא הוא UID שנוצר באופן אקראי. לכן, קוראי NFC לא צריכים להסתמך על UID של מכשירי HCE כאמצעי אימות או זיהוי.

לאחר מכן, קורא ה-NFC יכול לבחור את מכשיר ה-HCE על ידי שליחת הפקודה SEL_REQ. בתגובה SEL_RES של מכשיר ה-HCE מוגדר לפחות הביט השישי (0x20), שמציין שהמכשיר תומך ב-ISO-DEP. שימו לב שיכול להיות שגם ביטים אחרים ב-SEL_RES מוגדרים, למשל כדי לציין תמיכה בפרוטוקול NFC-DEP‏ (p2p). מכיוון שיכול להיות שביטים אחרים מוגדרים, קוראים שרוצים לקיים אינטראקציה עם מכשירי HCE צריכים לבדוק באופן מפורש את הביט השישי בלבד, ולא להשוות את SEL_RES המלא לערך 0x20.

הפעלת ISO-DEP

אחרי ההפעלה של פרוטוקול Nfc-A, קורא ה-NFC יוזם את הפעלת הפרוטוקול ISO-DEP. הוא שולח פקודה מסוג RATS (בקשה לתשובה לבחירה). בקר ה-NFC יוצר את התגובה של RATS, את ATS. לא ניתן להגדיר את ATS באמצעות שירותי HCE. עם זאת, הטמעות של HCE חייבות לעמוד בדרישות של NFC Forum לתגובה של ATS, כך שקוראי NFC יכולים לסמוך על כך שהפרמטרים האלה מוגדרים בהתאם לדרישות של NFC Forum לכל מכשיר HCE.

בקטע הבא מופיעים פרטים נוספים על הבייטים הנפרדים בתשובה של ATS שמספק בקר ה-NFC במכשיר HCE:

  • TL: אורך התגובה של ATS. אסור לציין אורך של יותר מ-20 בייט.
  • T0: יש להגדיר את הביטים 5, 6 ו-7 בכל מכשירי HCE, וכך לציין TA(1), TB(1) ו-TC(1) כלולים בתגובת ATS. הביטים 1 עד 4 מציינים את FSCI, שמקודד את גודל המסגרת המקסימלי. במכשירי HCE, הערך של FSCI צריך להיות בין 0h ל-8h.
  • T(A)1: הגדרת קצב הנתונים בין הקורא לבין המהדר, והאם הוא יכול להיות אסימטרי. אין דרישות או הבטחות לגבי קצב הנתונים במכשירי HCE.
  • T(B)1: הביטים 1 עד 4 מציינים את המספר השלם של זמן ההגנה על מסגרת ההתחלה (SFGI). במכשירי HCE, SFGI צריך להיות פחות מ-8 שעות. הביטים 5 עד 8 מציינים את המספר המלא של זמן ההמתנה לפרסום פריים (FWI) ומקודדים את זמן ההמתנה לפרסום פריים (FWT). במכשירי HCE, FWI חייב להיות פחות מ-8 שעות.
  • T(C)1: ביט 5 מציין תמיכה ב'תכונות פרוטוקול מתקדמות'. מכשירי HCE עשויים לתמוך, או שלא, ב'תכונות פרוטוקול מתקדמות'. ביט 2 מציין תמיכה ב-DID. יכול להיות שמכשירי HCE תומכים ב-DID ויכול להיות שלא. ביט 1 מציין תמיכה ב-NAD. מכשירים עם HCE לא יכולים לתמוך ב-NAD ולהגדיר את ביט 1 לאפס.
  • בייטים היסטוריים: מכשירים עם HCE עשויים להחזיר עד 15 בייטים היסטוריים. קוראי NFC שרוצים לקיים אינטראקציה עם שירותי HCE לא צריכים להניח דבר לגבי התוכן של הבייטים ההיסטוריים או נוכחותם.

שימו לב: סביר להניח שמכשירי HCE רבים עומדים בדרישות הפרוטוקולים שרשתות התשלומים המאוחדות ב-EMVCo ציינו במפרט של פרוטוקול 'פרוטוקול התקשורת ללא מגע'. ובפרט:

  • זמן ה-FSCI ב-T0 צריך להיות בין שעתיים ל-8 שעות.
  • הערך T(A)1 צריך להיות מוגדר ל-0x80, שמציין רק שיש תמיכה בקצב העברת נתונים של 106 kbit/s, ושבקצבי העברת נתונים אסימטריים בין הקורא לאמולטור אין תמיכה.
  • FWI ב-T(B)1 חייב להיות <= 7 שעות.

החלפת נתונים ב-APDU

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