תמונות של מערכת ההפעלה Android משתמשות בחתימות קריפטוגרפיות בשני מקומות:
- כל קובץ
.apk
בתוך התמונה צריך להיות חתום. מנהל החבילות של Android משתמש בחתימה.apk
בשתי דרכים:- כשמחליפים אפליקציה, צריך לחתום על האפליקציה החדשה באמצעות אותו מפתח ששימש לחתימה על האפליקציה הישנה, כדי לקבל גישה לנתונים של האפליקציה הישנה. הדבר נכון גם לעדכון אפליקציות של משתמשים על ידי החלפת
.apk
, וגם להחלפת אפליקציית מערכת בגרסה חדשה יותר שמותקנת ב-/data
. - אם רוצים ששתי אפליקציות או יותר ישתפו מזהה משתמש (כדי שיוכלו לשתף נתונים וכו'), צריך לחתום עליהן עם אותו מפתח.
- כשמחליפים אפליקציה, צריך לחתום על האפליקציה החדשה באמצעות אותו מפתח ששימש לחתימה על האפליקציה הישנה, כדי לקבל גישה לנתונים של האפליקציה הישנה. הדבר נכון גם לעדכון אפליקציות של משתמשים על ידי החלפת
- חבילות עדכון OTA חייבות להיות חתומות באחד מהמפתחות שהמערכת מצפה להם, אחרת תהליך ההתקנה ידחה אותן.
מפתחות גרסה
עץ Android כולל test-keys בקטע
build/target/product/security
. כשיוצרים אימג' של מערכת הפעלה Android באמצעות make
, כל הקבצים של .apk
נחתמים באמצעות מפתחות הבדיקה. מכיוון שמפתחות הבדיקה ידועים לציבור, כל אחד יכול לחתום איתם על קובצי ה-.apk שלו, מה שעלול לאפשר לו להחליף או להשתלט על אפליקציות מערכת שמוטמעות בתמונת מערכת ההפעלה. לכן חשוב מאוד לחתום על כל תמונה של מערכת ההפעלה Android שמופצת או נפרסת באופן ציבורי באמצעות קבוצה מיוחדת של מפתחות הפצה, שרק לכם יש גישה אליהם.
כדי ליצור קבוצה ייחודית משלכם של מפתחות חתימה, מריצים את הפקודות הבאות מהשורש של עץ 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
מקבל קובץ target-files
.zip
כקלט ומפיק קובץ חדש target-files .zip
שבו
כל הקבצים .apk
נחתמו באמצעות מפתחות חדשים. התמונות החדשות עם החתימה נמצאות בתיקייה IMAGES/
בתוך signed-target_files.zip
.
חתימה על חבילות OTA
אפשר להמיר קובץ ZIP חתום של target-files לקובץ ZIP חתום של עדכון OTA באמצעות התהליך הבא:
ota_from_target_files \
-k (--package_key)
signed-target_files.zip \
signed-ota_update.zip
חתימות והעלאה צדדית
העברה צדדית לא עוקפת את מנגנון האימות הרגיל של חתימת החבילה של שחזור המערכת – לפני התקנת חבילה, שחזור המערכת יאמת שהיא חתומה באחד מהמפתחות הפרטיים שתואמים למפתחות הציבוריים שמאוחסנים במחיצת השחזור, בדיוק כמו במקרה של חבילה שמועברת דרך האוויר.
בדרך כלל, חבילות עדכון שמתקבלות מהמערכת הראשית מאומתות פעמיים: פעם אחת על ידי המערכת הראשית, באמצעות ה-method RecoverySystem.verifyPackage()
ב-Android API, ופעם נוספת על ידי recovery. 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
- מפתח ברירת מחדל כללי לחבילות שלא מצוין בהן מפתח אחר.
- פלטפורמה
- מפתח בדיקה לחבילות שכלולות בפלטפורמה המרכזית.
- משותפת
- מפתח בדיקה לדברים שמשותפים בתהליך של הבית או אנשי הקשר.
- מדיה
- מפתח בדיקה לחבילות שמשתייכות למערכת המדיה או ההורדה.
חבילות בודדות מציינות אחד מהמפתחות האלה על ידי הגדרת LOCAL_CERTIFICATE בקובץ Android.mk שלהן. (אם המשתנה הזה לא מוגדר, נעשה שימוש ב-testkey). אפשר גם לציין מפתח שונה לחלוטין לפי שם הנתיב, למשל:
device/yoyodyne/apps/SpecialApp/Android.mk
[...] LOCAL_CERTIFICATE := device/yoyodyne/security/special
מעכשיו, תהליך הבנייה משתמש במפתח device/yoyodyne/security/special.{x509.pem,pk8}
כדי לחתום על SpecialApp.apk. ב-build אפשר להשתמש רק במפתחות פרטיים שלא מוגנים באמצעות סיסמה.
אפשרויות מתקדמות לחתימה
החלפה של מפתח לחתימת APK
סקריפט החתימה sign_target_files_apks
פועל על קובצי היעד שנוצרו בשביל 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 שצוינו כקובצי 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
למטען הייעודי של תמונת הקובץ).
המשתמש 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 של 2,048 ביט עם מעריך ציבורי 3. אפשר ליצור זוגות של אישורים ומפתחות פרטיים באמצעות הכלי openssl מ-openssl.org:
# generate RSA keyopenssl 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 keyopenssl 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 keyopenssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt
# securely delete the temp.pem fileshred --remove temp.pem
הפקודה openssl pkcs8 שצוינה למעלה יוצרת קובץ .pk8 ללא סיסמה, שמתאים לשימוש במערכת ה-build. כדי ליצור קובץ .pk8 מאובטח עם סיסמה (מומלץ לעשות זאת לכל מפתחות ההפצה בפועל), מחליפים את הארגומנט -nocrypt
בארגומנט -passout stdin
. לאחר מכן, openssl יצפין את המפתח הפרטי באמצעות סיסמה שנקראת מקלט סטנדרטי. לא מוצגת הנחיה, ולכן אם stdin הוא הטרמינל, התוכנית תיראה כאילו היא נתקעת, כשבפועל היא רק מחכה שתזינו סיסמה. אפשר להשתמש בערכים אחרים לארגומנט passout כדי לקרוא את הסיסמה ממיקומים אחרים. לפרטים, אפשר לעיין ב
מסמכי התיעוד של openssl.
קובץ הביניים 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 באופן הבא:
fastboot update signed-img.zip