סכימת חתימות APK v2

APK Signature Scheme v2 היא סכימת חתימה של קובץ שלם שמגבירה את מהירות האימות ומחזקת ערבויות שלמות על ידי זיהוי כל שינוי בחלקים המוגנים של ה-APK.

חתימה באמצעות APK Signature Scheme v2 מכניסה בלוק חתימת APK לקובץ ה-APK מיד לפני הקטע של ZIP Central Directory. בתוך בלוק החתימה של APK, חתימות v2 ופרטי זהות החותם מאוחסנים ב- APK Signature Scheme v2 Block .

APK לפני ואחרי החתימה

איור 1. APK לפני ואחרי החתימה

APK Signature Scheme v2 הוצג באנדרואיד 7.0 (Nougat). כדי להפוך APK להתקנה במכשירי אנדרואיד 6.0 (Marshmallow) ומעלה, יש לחתום על ה-APK באמצעות חתימת JAR לפני החתימה עם ערכת v2.

חסימת חתימת APK

כדי לשמור על תאימות לאחור עם פורמט ה-APK v1, חתימות APK v2 וחדש יותר מאוחסנות בתוך בלוק חתימת APK, מיכל חדש שהוצג לתמיכה ב-APK Signature Scheme v2. בקובץ APK, בלוק החתימה של APK ממוקם מיד לפני המדריך המרכזי של ZIP, שנמצא בסוף הקובץ.

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

פוּרמָט

הפורמט של בלוק החתימה APK הוא כדלקמן (כל השדות המספריים הם מעט אנדיאנים):

  • size of block בבתים (לא כולל שדה זה) (uint64)
  • רצף של צמדי ערך מזהה-ערך באורך uint64:
    • ID (uint32)
    • value (אורך משתנה: אורך הזוג - 4 בתים)
  • size of block בבתים - זהה לשדה הראשון (uint64)
  • magic "APK Sig Block 42" (16 בתים)

ה-APK מנותח על ידי מציאת תחילה את ההתחלה של ה-ZIP Central Directory (על ידי מציאת רשומת ZIP End of Central Directory בסוף הקובץ, ולאחר מכן קריאת היסט ההתחלה של ה-Central Directory מהרשומה). ערך magic מספק דרך מהירה לקבוע שמה שקודם ל-Central Directory הוא כנראה בלוק החתימה של APK. size of block מצביע אז ביעילות על תחילת הבלוק בקובץ.

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

APK Signature Scheme v2 Block

APK חתום על ידי חתם/זהות אחד או יותר, כל אחד מיוצג על ידי מפתח חתימה. מידע זה מאוחסן כ-APK Signature Scheme v2 Block. עבור כל חותם, המידע הבא מאוחסן:

  • (אלגוריתם חתימה, תקציר, חתימה) tuples. התקציר מאוחסן כדי לנתק את אימות החתימה מבדיקת תקינות התוכן של ה-APK.
  • שרשרת תעודות X.509 המייצגת את זהותו של החותם.
  • מאפיינים נוספים כצמדי מפתח-ערך.

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

פוּרמָט

APK Signature Scheme v2 Block מאוחסן בתוך בלוק החתימה APK תחת המזהה 0x7109871a .

הפורמט של בלוק ה-APK Signature Scheme v2 הוא כדלקמן (כל הערכים המספריים הם Little-endian, כל השדות עם קידומת אורך משתמשים ב-uint32 עבור האורך):

  • רצף עם קידומת אורך של signer עם קידומת אורך:
    • signed data עם קידומת אורך:
      • רצף עם קידומת אורך של digests עם קידומת אורך:
      • רצף עם קידומת אורך של certificates X.509 :
        • certificate X.509 עם קידומת אורך (טופס DER ASN.1)
      • רצף עם קידומת אורך של additional attributes עם קידומת אורך:
        • ID (uint32)
        • value (אורך משתנה: אורך התכונה הנוספת - 4 בתים)
    • רצף של signatures עם קידומת אורך:
      • signature algorithm ID (uint32)
      • signature עם קידומת אורך על פני signed data
    • public key עם קידומת אורך (SubjectPublicKeyInfo, טופס ASN.1 DER)

מזהי אלגוריתם חתימה

  • 0x0101—RSASSA-PSS עם SHA2-256 digest, SHA2-256 MGF1, 32 בייטים של מלח, טריילר: 0xbc
  • 0x0102—RSASSA-PSS עם SHA2-512 digest, SHA2-512 MGF1, 64 בייטים של מלח, טריילר: 0xbc
  • 0x0103—RSASSA-PKCS1-v1_5 עם תקציר SHA2-256. זה מיועד לבניית מערכות הדורשות חתימות דטרמיניסטיות.
  • 0x0104—RSASSA-PKCS1-v1_5 עם תקציר SHA2-512. זה מיועד לבניית מערכות הדורשות חתימות דטרמיניסטיות.
  • 0x0201—ECDSA עם תקציר SHA2-256
  • 0x0202—ECDSA עם תקציר SHA2-512
  • 0x0301—DSA עם תקציר SHA2-256

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

גדלי מפתחות נתמכים ועקומות EC:

  • RSA: 1024, 2048, 4096, 8192, 16384
  • EC: NIST P-256, P-384, P-521
  • DSA: 1024, 2048, 3072

תוכן מוגן בשלמות

למטרות הגנה על תוכן APK, APK מורכב מארבעה חלקים:

  1. התוכן של ערכי ZIP (מהיסט 0 עד תחילת חסימת ה-APK)
  2. חסימת חתימת APK
  3. מדריך ZIP מרכזי
  4. ZIP סוף של ספרייה מרכזית

קטעי APK לאחר החתימה

איור 2. חלקי APK לאחר החתימה

APK Signature Scheme v2 מגן על שלמות סעיפים 1, 3, 4, ובלוקי signed data של בלוק APK Signature Scheme v2 הכלולים בסעיף 2.

תקינותם של סעיפים 1, 3 ו-4 מוגנת על ידי תקציר אחד או יותר של תוכנם המאוחסן בבלוק signed data אשר, בתורם, מוגנים על ידי חתימה אחת או יותר.

התקציר על סעיפים 1, 3 ו-4 מחושב באופן הבא, בדומה לעץ מרקל דו-מפלסי. כל קטע מחולק לנתחים רצופים של 1 MB (2 20 בתים). הנתח האחרון בכל חלק עשוי להיות קצר יותר. התקציר של כל נתח מחושב על פני השרשור של byte 0xa5 , אורך הנתח בבתים (Uint32-little-endian), ותוכן הנתח. התקציר ברמה העליונה מחושב על-פי השרשור של byte 0x5a , מספר הנתחים (ליטל-endian uint32), והשרשור של תקצירים של הנתחים לפי סדר הופעת הנתחים ב-APK. התקציר מחושב בצורה חתוכה כדי לאפשר להאיץ את החישוב על ידי הקבלה שלו.

תקציר APK

איור 3. תקציר APK

ההגנה על סעיף 4 (ZIP End of Central Directory) מסובכת על ידי הסעיף המכיל את ההיסט של ZIP Central Directory. ההיסט משתנה כאשר גודל בלוק החתימה APK משתנה, למשל, כאשר מתווספת חתימה חדשה. לפיכך, כאשר מחשבים תקציר דרך ZIP End של Central Directory, יש להתייחס לשדה המכיל את ההיסט של ZIP Central Directory כמכיל את ההיסט של בלוק החתימה של APK.

הגנות לאחור

תוקף יכול לנסות לאמת APK עם חתימת v2 כ-APK עם חתימת v1 בפלטפורמות אנדרואיד התומכות באימות APK עם חתימת v2. כדי למתן את ההתקפה הזו, חבילות APK עם חתימת v2 שגם הם חתומים ב-v1 חייבים להכיל תכונה X-Android-APK-Signed בחלק הראשי של קבצי META-INF/*.SF שלהם. הערך של התכונה הוא קבוצה מופרדת בפסיקים של מזהי סכימת חתימות APK (המזהה של סכמה זו הוא 2). בעת אימות חתימת v1, מאמת APK נדרש לדחות חבילות APK שאין להן חתימה עבור סכימת חתימת APK שהמאמת מעדיף מקבוצה זו (למשל, סכימת v2). הגנה זו מסתמכת על העובדה שקובצי התוכן META-INF/*.SF מוגנים על ידי חתימות v1. עיין בסעיף על אימות APK חתום על JAR .

תוקף יכול לנסות להסיר חתימות חזקות יותר מה-APK Signature Scheme v2 Block. כדי למתן את ההתקפה הזו, רשימת מזהי אלגוריתמי החתימה שאיתם ה-APK נחתם מאוחסנת בבלוק signed data המוגן על ידי כל חתימה.

אימות

ב-Android 7.0 ואילך, ניתן לאמת חבילות APK בהתאם ל-APK Signature Scheme v2+ או חתימת JAR (סכימת v1). פלטפורמות ישנות יותר מתעלמות מחתימות v2 ומאמתות רק חתימות v1.

תהליך אימות חתימת APK

איור 4. תהליך אימות חתימת APK (שלבים חדשים באדום)

אימות APK Signature Scheme v2

  1. אתר את בלוק החתימה של APK וודא ש:
    1. שני שדות בגודל של APK Signing Block מכילים את אותו ערך.
    2. ZIP Central Directory מלווה מיד ב-ZIP End של רשומת ה-Central Directory.
    3. ZIP End of Central Directory אינו מלווה במידע נוסף.
  2. אתר את בלוק ה-APK Signature Scheme v2 הראשון בתוך בלוק החתימה APK. אם החסימה של v2 אם קיימת, המשך לשלב 3. אחרת, חזור לאימות ה-APK באמצעות סכימת v1 .
  3. עבור כל signer ב-APK Signature Scheme v2 Block:
    1. בחר את signature algorithm ID הנתמך החזק ביותר מתוך signatures . סדר החוזק תלוי בכל גרסת יישום/פלטפורמה.
    2. אמת את signature המתאימה signatures מול signed data באמצעות public key . (עכשיו זה בטוח לנתח signed data ).
    3. ודא שהרשימה המסודרת של מזהי אלגוריתמי החתימות digests signatures זהה. (זה כדי למנוע הפשטה/הוספה של חתימות.)
    4. חשב את התקציר של תוכן ה-APK באמצעות אותו אלגוריתם תקציר כמו אלגוריתם התקציר המשמש את אלגוריתם החתימה.
    5. ודא שהתקציר המחושב זהה digest המתאים digests .
    6. ודא ש-SubjectPublicKeyInfo של certificate הראשון של certificates זהה public key .
  4. האימות יצליח אם נמצא לפחות signer אחד ושלב 3 הצליח עבור כל signer שנמצא.

הערה : אין לאמת APK באמצעות סכימת v1 אם מתרחשת כשל בשלב 3 או 4.

אימות APK חתום על JAR (סכימת v1)

ה-APK החתום על JAR הוא JAR חתום סטנדרטי , שחייב להכיל בדיוק את הערכים הרשומים ב- META-INF/MANIFEST.MF ושם כל הערכים חייבים להיות חתומים על ידי אותה קבוצה של חותמים. תקינותו מאומתת באופן הבא:

  1. כל חותם מיוצג על ידי ערך META-INF/<חותם>.SF ו- META-INF/<חותם>.(RSA|DSA|EC) JAR.
  2. <signer>.(RSA|DSA|EC) הוא PKCS #7 CMS ContentInfo עם מבנה SignedData שהחתימה שלו מאומתת בקובץ <signer>.SF.
  3. קובץ <signer>.SF מכיל תקציר של קובץ שלם של META-INF/MANIFEST.MF ותקצירים של כל מקטע של META-INF/MANIFEST.MF. תקציר הקובץ כולו של MANIFEST.MF מאומת. אם זה נכשל, התקציר של כל סעיף MANIFEST.MF מאומת במקום זאת.
  4. META-INF/MANIFEST.MF מכיל, עבור כל ערך JAR מוגן בשלמות, קטע בעל שם מתאים המכיל את תקציר התוכן הלא דחוס של הערך. כל העיכולים הללו מאומתים.
  5. אימות APK נכשל אם ה-APK מכיל ערכי JAR שאינם רשומים ב-MANIFEST.MF ואינם חלק מחתימת JAR.

שרשרת ההגנה היא לפיכך <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> התוכן של כל ערך JAR מוגן בשלמות.