חתימה על גרסאות build לגרסה

בתמונות ל-Android OS נעשה שימוש בחתימות קריפטוגרפיות בשני מקומות:

  1. כל קובץ .apk שנמצא בתמונה חייב להיות חתום. של Android Package Manager משתמש בחתימת .apk בשתי דרכים:
    • כשמחליפים אפליקציה, צריך לחתום עליה באותו מפתח כמו של האפליקציה הישנה כדי לקבל גישה לנתונים של האפליקציה הישנה. המשמעות היא True גם לעדכון אפליקציות של משתמשים על ידי החלפה של .apk וגם החלפה של אפליקציית מערכת שמותקנת בה גרסה חדשה יותר /data.
    • אם שתי אפליקציות או יותר רוצות לשתף מזהה משתמש (כדי שיוכלו לשתף וכו'), הם צריכים להיות חתומים באותו מפתח.
  2. חבילות עדכון OTA חייבות להיות חתומות עם אחד מהמפתחות הנדרשים על ידי המערכת או תהליך ההתקנה ידחה אותן.

מקשי הגרסה

עץ Android כולל מפתחות בדיקה בקטע build/target/product/security. יצירת קובץ אימג' ל-Android OS באמצעות make יחתום על כל .apk הקבצים באמצעות למפתחות בדיקה. מאחר שמפתחות הבדיקה ידועים לציבור, כל אחד יכול לחתום בעצמו בקובצי .APK עם אותם מפתחות, דבר שעלול לאפשר להם להחליף את המערכת או לפרוץ אליה אפליקציות מובנות בקובץ האימג' של מערכת ההפעלה. לכן חשוב לחתום על תמונה של Android OS שהושקה באופן ציבורי או נפרסה עם קבוצה מיוחדת של מפתחות הפצה שרק לכם יש גישה אליהם.

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

subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
mkdir ~/.android-certs
for x in releasekey platform shared media networkstack; do \
    ./development/tools/make_key ~/.android-certs/$x "$subject"; \
  done

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

כדי ליצור קובץ אימג' של פריט תוכן, צריך להשתמש ב:

make dist
sign_target_files_apks \
-o \    # explained in the next section
--default_key_mappings ~/.android-certs out/dist/*-target_files-*.zip \
signed-target_files.zip

הסקריפט sign_target_files_apks לוקח קובצי יעד את הפקודה .zip כקלט ומפיק קובצי יעד חדשים .zip ב- שכל .apk הקבצים נחתמו באמצעות מפתחות חדשים. הגרסה החדשה ניתן למצוא תמונות חתומות תחת IMAGES/ ב: signed-target_files.zip.

חתימה על חבילות OTA

אפשר להמיר קובץ ZIP של קובצי יעד חתומים לקובץ ZIP חתום עם עדכון OTA באמצעות התהליך הבא:
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

חתימות והתקנה ממקור לא ידוע

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

חבילות עדכון שמתקבלות מהמערכת הראשית מאומתות בדרך כלל פעמיים: פעם אחת על ידי המערכת הראשית, באמצעות RecoverySystem.verifyPackage() ב-API ל-Android, ואז שוב באמצעות תהליך השחזור. ממשק ה-API של RecoverSystem API בודק את החתימה מול מפתחות ציבוריים מאוחסנים במערכת הראשית, בקובץ /system/etc/security/otacerts.zip (כברירת מחדל). השחזור בודק את החתימה מול מפתחות ציבוריים שמאוחסנים בדיסק ה-RAM של מחיצת השחזור, בקובץ /res/keys.

כברירת מחדל, קובצי היעד .zip שה-build יוצר מגדירים אישור OTA להתאמה למפתח הבדיקה. בתמונה שפורסמה, כדי שמכשירים יוכלו לאמת את האותנטיות של עדכון החבילה. העברת הדגל -o אל sign_target_files_apks, כמו שמוצג בקטע הקודם, מחליף את אישור של מפתח הבדיקה עם האישור של מפתח ההפצה מהאישורים שלך

בדרך כלל תמונת המערכת ותמונת השחזור מאחסנים את אותה קבוצה של OTA מפתחות ציבוריים. הוספת מפתח רק לקבוצת המפתחות לשחזור, לחתום על חבילות שאפשר להתקין רק באמצעות התקנה ממקור לא ידוע. (בהנחה שמנגנון הורדת העדכונים במערכת הראשית פועל כראוי לאימות מול otacerts.zip). אפשר לציין מפתחות נוספים שיהיו נכללות רק בשחזור על ידי הגדרה של PRODUCT_עביר_Recover_KEYS בהגדרת המוצר:

vendor/yoyodyne/tardis/products/tardis.mk
 [...]

PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload

כולל המפתח הציבורי vendor/yoyodyne/security/tardis/sideload.x509.pem בשחזור של המפתחות כדי להתקין חבילות שנחתמו איתו. עם זאת, המפתח הנוסף לא כלול ב-otacerts.zip, לכן במערכות שמאמתות בצורה תקינה חבילות שהורדו לא מפעילים שחזור חבילות שנחתמו באמצעות המפתח הזה.

אישורים ומפתחות פרטיים

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

גרסת ה-build הרגילה של Android משתמשת בחמישה מפתחות, שכולם נמצאים ב- build/target/product/security:

מפתח בדיקה
מפתח ברירת מחדל גנרי לחבילות שלא מצוין בהן מפתח אחר.
פלטפורמה
מפתח בדיקה לחבילות שהן חלק מפלטפורמת הליבה.
משותף
מפתח בדיקה של דברים שמשותפים בתהליך הבית או אנשי הקשר.
מדיה
מפתח בדיקה של חבילות שהן חלק ממערכת המדיה/ההורדה.
Networktack
מפתח בדיקה לחבילות שהן חלק ממערכת הרשת. מפתח Networkstack משמש לחתימה על קבצים בינאריים שתוכננו רכיבים למערכות מודולריות . אם עדכוני המודול נוצרים בנפרד ומשולבים כמבנים מראש בתמונת המכשיר, ייתכן שלא יהיה צורך ליצור מפתח Networktack עץ המקור של Android.

חבילות בודדות מציינות אחד מהמפתחות האלה על ידי הגדרה של LOCAL_CERTIFICATE בקובץ Android.mk שלהם. (אם המשתנה הזה לא מוגדר, המערכת תשתמש במפתח בדיקה). שלך הוא יכול גם לציין מפתח שונה לגמרי לפי שם נתיב, לדוגמה:

device/yoyodyne/apps/SpecialApp/Android.mk
 [...]

LOCAL_CERTIFICATE := device/yoyodyne/security/special

עכשיו ה-build משתמש במקש device/yoyodyne/security/special.{x509.pem,pk8} כדי לחתום על SpecialApp.APK. ה-build יכול להשתמש רק במפתחות פרטיים אינם מוגנים באמצעות סיסמה.

אפשרויות חתימה מתקדמות

החלפה של חתימת APK

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

אפשר להשתמש ב--key_mapping וב--default_key_mappings דגלים לציון החלפת מפתחות על סמך שמות מפתחות:

  • הדגל --key_mapping src_key=dest_key מציין החלפה של מפתח אחד בכל פעם.
  • הדגל --default_key_mappings dir מציין לספרייה עם חמישה מפתחות שיחליפו את כל המפתחות build/target/product/security; שוות ערך לשימוש --key_mapping חמש פעמים כדי לציין את המיפויים.
build/target/product/security/testkey      = dir/releasekey
build/target/product/security/platform     = dir/platform
build/target/product/security/shared       = dir/shared
build/target/product/security/media        = dir/media
build/target/product/security/networkstack = dir/networkstack

משתמשים ב דגל --extra_apks apk_name1,apk_name2,...=key כדי לציין את החלפות של מפתחות החתימה על סמך שמות חבילות APK. אם המיקום השדה key נשאר ריק, הסקריפט מטפל בחבילות ה-APK שצוינו כמו שנחתמו מראש.

למוצר ההיפותטי tardis צריך שישה מפתחות שמוגנים בסיסמה: חמש כדי להחליף את חמש הספרות בbuild/target/product/security ואחת כדי להחליף את המפתח הנוסף device/yoyodyne/security/special שנדרשת על ידי SpecialApp בדוגמה שלמעלה. אם המפתחות היו קבצים:

vendor/yoyodyne/security/tardis/releasekey.x509.pem
vendor/yoyodyne/security/tardis/releasekey.pk8
vendor/yoyodyne/security/tardis/platform.x509.pem
vendor/yoyodyne/security/tardis/platform.pk8
vendor/yoyodyne/security/tardis/shared.x509.pem
vendor/yoyodyne/security/tardis/shared.pk8
vendor/yoyodyne/security/tardis/media.x509.pem
vendor/yoyodyne/security/tardis/media.pk8
vendor/yoyodyne/security/tardis/networkstack.x509.pem
vendor/yoyodyne/security/tardis/networkstack.pk8
vendor/yoyodyne/security/special.x509.pem
vendor/yoyodyne/security/special.pk8           # NOT password protected
vendor/yoyodyne/security/special-release.x509.pem
vendor/yoyodyne/security/special-release.pk8   # password protected

לאחר מכן, צריך לחתום על כל האפליקציות באופן הבא:

./build/make/tools/releasetools/sign_target_files_apks \
    --default_key_mappings vendor/yoyodyne/security/tardis \
    --key_mapping vendor/yoyodyne/security/special=vendor/yoyodyne/security/special-release \
    --extra_apks PresignedApp= \
    -o tardis-target_files.zip \
    signed-tardis-target_files.zip

התוצאה היא:

Enter password for vendor/yoyodyne/security/special-release key>
Enter password for vendor/yoyodyne/security/tardis/networkstack key>
Enter password for vendor/yoyodyne/security/tardis/media key>
Enter password for vendor/yoyodyne/security/tardis/platform key>
Enter password for vendor/yoyodyne/security/tardis/releasekey key>
Enter password for vendor/yoyodyne/security/tardis/shared key>
    signing: Phone.apk (vendor/yoyodyne/security/tardis/platform)
    signing: Camera.apk (vendor/yoyodyne/security/tardis/media)
    signing: NetworkStack.apk (vendor/yoyodyne/security/tardis/networkstack)
    signing: Special.apk (vendor/yoyodyne/security/special-release)
    signing: Email.apk (vendor/yoyodyne/security/tardis/releasekey)
        [...]
    signing: ContactsProvider.apk (vendor/yoyodyne/security/tardis/shared)
    signing: Launcher.apk (vendor/yoyodyne/security/tardis/shared)
NOT signing: PresignedApp.apk
        (skipped due to special cert string)
rewriting SYSTEM/build.prop:
  replace:  ro.build.description=tardis-user Eclair ERC91 15449 test-keys
     with:  ro.build.description=tardis-user Eclair ERC91 15449 release-keys
  replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys
     with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys
    signing: framework-res.apk (vendor/yoyodyne/security/tardis/platform)
rewriting RECOVERY/RAMDISK/default.prop:
  replace:  ro.build.description=tardis-user Eclair ERC91 15449 test-keys
     with:  ro.build.description=tardis-user Eclair ERC91 15449 release-keys
  replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys
     with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys
using:
    vendor/yoyodyne/security/tardis/releasekey.x509.pem
for OTA package verification
done.

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

החלפת מפתח חתימה ל-APEX

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

כשחותמים על גרסה, שני מפתחות החתימה של קובץ APEX מוחלפים באמצעות מקשי ההפצה. מפתח המטען הייעודי (payload) של מערכת הקבצים מצוין עם הדגל --extra_apex_payload ומפתח החתימה של קובץ APEX כולו הוא שצוין בדגל --extra_apks.

במוצר tardis, נניח שיש לכם את הגדרות המפתח הבאות בשביל com.android.conscrypt.apex, com.android.media.apex, וגם com.android.runtime.release.apex קובצי APEX.

name="com.android.conscrypt.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED"
name="com.android.media.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED"
name="com.android.runtime.release.apex" public_key="vendor/yoyodyne/security/testkeys/com.android.runtime.avbpubkey" private_key="vendor/yoyodyne/security/testkeys/com.android.runtime.pem" container_certificate="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.x509.pem" container_private_key="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.pk8"

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

vendor/yoyodyne/security/runtime_apex_container.x509.pem
vendor/yoyodyne/security/runtime_apex_container.pk8
vendor/yoyodyne/security/runtime_apex_payload.pem

הפקודה הבאה מבטלת את מפתחות החתימה עבור com.android.runtime.release.apex והקבוצה com.android.tzdata.apex במהלך חתימת הגרסה. באופן ספציפי, com.android.runtime.release.apex נחתם עם שצוין מפתחות הפצה (runtime_apex_container לקובץ ה-APEX, runtime_apex_payload למטען הייעודי (payload) של קובץ התמונה). הפונקציה com.android.tzdata.apex נחשבת כחתימה מראש. כל שאר נתוני APEX הקבצים מטופלים על ידי הגדרות ברירת המחדל כפי שמפורט בקובצי היעד.

./build/make/tools/releasetools/sign_target_files_apks \
    --default_key_mappings   vendor/yoyodyne/security/tardis \
    --extra_apks             com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_container \
    --extra_apex_payload_key com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_payload.pem \
    --extra_apks             com.android.media.apex= \
    --extra_apex_payload_key com.android.media.apex= \
    -o tardis-target_files.zip \
    signed-tardis-target_files.zip

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

        [...]
    signing: com.android.runtime.release.apex                  container (vendor/yoyodyne/security/runtime_apex_container)
           : com.android.runtime.release.apex                  payload   (vendor/yoyodyne/security/runtime_apex_payload.pem)
NOT signing: com.android.conscrypt.apex
        (skipped due to special cert string)
NOT signing: com.android.media.apex
        (skipped due to special cert string)
        [...]

אפשרויות אחרות

סקריפט החתימה sign_target_files_apks משכתב את ה-build וטביעת האצבע בקובצי מאפייני ה-build, כדי לשקף build הוא build חתום. הדגל --tag_changes קובע אילו פעולות עריכה לטביעת האצבע. מריצים את הסקריפט באמצעות -h כדי לראות תיעוד של כל הדגלים.

יצירת מפתחות באופן ידני

מערכת Android משתמשת במפתחות RSA של 2048 ביט עם מעריך ציבורי 3. אפשר ליצור אישור/זוג מפתחות פרטיים באמצעות הכלי opensl מ- openssl.org:

# generate RSA key
openssl genrsa -3 -out temp.pem 2048
Generating RSA private key, 2048 bit long modulus
....+++
.....................+++
e is 3 (0x3)

# create a certificate with the public part of the key
openssl req -new -x509 -key temp.pem -out releasekey.x509.pem -days 10000 -subj '/C=US/ST=California/L=San Narciso/O=Yoyodyne, Inc./OU=Yoyodyne Mobility/CN=Yoyodyne/emailAddress=yoyodyne@example.com'

# create a PKCS#8-formatted version of the private key
openssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt

# securely delete the temp.pem file
shred --remove temp.pem

פקודת ה-Opensl pkcs8 שצוינה למעלה יוצרת קובץ .pk8 עם הערך no סיסמה שמתאימה לשימוש עם מערכת ה-build. כדי ליצור קובץ .pk8 מאובטח בסיסמה (שפעולה זו נדרשת עבור כל מפתחות ההפצה בפועל), מחליפים את ארגומנט -nocrypt עם -passout stdin; לאחר מכן opensl יצפין את המפתח הפרטי באמצעות סיסמה שתיקרא מהקלט הרגיל. לא מודפסת, כך שאם stdin הוא הטרמינל, התוכנה תיראה תלויה כשהוא רק מחכה שתכניסו את הסיסמה. ערכים אחרים יכולים להיות משמש לארגומנט 'pass' כדי לקרוא את הסיסמה ממיקומים אחרים; עבור פרטים נוספים, עיינו במאמר תיעוד ה-Opensl.

קובץ הביניים temp.pem מכיל את המפתח הפרטי ללא כל סוג של הגנה באמצעות סיסמה, לכן יש להשליך אותה בזהירות בעת יצירת גרסה מקשי קיצור. בפרט, ייתכן שהכלי GNUshred לא יהיה יעיל ברשת מערכות קבצים שנרשמו ביומן. אפשר להשתמש בספריית עבודה שנמצאת בדיסק RAM (למשל מחיצת tmpfs) כשיוצרים מפתחות כדי להבטיח את זמני הביניים הם לא נחשפים בטעות.

יצירת קובצי תמונה

כשיש לך signed-target_files.zip, עליך: יוצרים את התמונה כדי שאפשר יהיה לשים אותה במכשיר. כדי ליצור את התמונה החתומה מקובצי היעד, מריצים את הפקודה הפקודה הבאה מהרמה הבסיסית (root) של Android עץ:

img_from_target_files signed-target_files.zip signed-img.zip
הקובץ שמתקבל, signed-img.zip, מכיל את כל הקבצים מסוג .img. כדי לטעון תמונה למכשיר, צריך להשתמש באתחול מהיר בתור ככה:
fastboot update signed-img.zip