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

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

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

מקשי הגרסה

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

כדי ליצור קבוצה ייחודית משלכם של מפתחות release, מריצים את הפקודות הבאות מהשורש של עץ 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 כך שישקף את הפרטים של הארגון. אפשר להשתמש בכל ספרייה, אבל חשוב לבחור מיקום מאובטח שגובה. ספקים מסוימים בוחרים להצפין את המפתח הפרטי שלהם באמצעות ביטוי סיסמה חזק ולאחסן את המפתח המוצפן במערכת בקרת הגרסאות. ספקים אחרים שומרים את מפתחות הגרסה שלהם במקום אחר לגמרי, למשל במחשב עם הפרדה פיזית (air-gap).

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

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 מקבל כקלט את target-files‏ .zip ויוצר target-files חדש .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, ואז שוב באמצעות תהליך השחזור. ‏RecoverySystem API בודק את החתימה מול מפתחות ציבוריים שמאוחסנים במערכת הראשית, בקובץ /system/etc/security/otacerts.zip (כברירת מחדל). בתהליך השחזור, המערכת בודקת את החתימה מול מפתחות ציבוריים שמאוחסנים בדיסק ה-RAM של מחיצת השחזור, בקובץ /res/keys.

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

בדרך כלל תמונת המערכת ותמונת השחזור מאחסנים את אותה קבוצה של OTA מפתחות ציבוריים. הוספת מפתח רק לקבוצת המפתחות לשחזור, לחתום על חבילות שאפשר להתקין רק באמצעות התקנה ממקור לא ידוע. (בהנחה שמנגנון הורדת העדכונים במערכת הראשית פועל כראוי לאימות מול otacerts.zip). כדי לציין מפתחות נוספים שייכללו רק בתהליך השחזור, מגדירים את המשתנה PRODUCT_EXTRA_RECOVERY_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:

testkey
מפתח ברירת מחדל גנרי לחבילות שלא מצוין בהן מפתח אחר.
פלטפורמה
מפתח בדיקה לחבילות שנכללות בפלטפורמת הליבה.
משותף
מפתח בדיקה של דברים שמשותפים בתהליך הבית או אנשי הקשר.
מדיה
מפתח בדיקה של חבילות שהן חלק ממערכת המדיה/ההורדה.
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 מוחלפים במפתחות גרסה. מפתח העומס של מערכת הקבצים מצוין באמצעות הדגל --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 לתוכן של קובץ התמונה). המערכת מתייחסת ל-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

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

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

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

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

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