Подписать сборки для выпуска

Образы ОС Android используют криптографические подписи в двух местах:

  1. Каждый файл .apk внутри образа должен быть подписан. Менеджер пакетов Android использует подпись .apk двумя способами:
    • При замене приложения оно должно быть подписано тем же ключом, что и старое приложение, чтобы получить доступ к его данным. Это справедливо как для обновления пользовательских приложений путём перезаписи .apk , так и для переопределения системного приложения новой версией, установленной в каталоге /data .
    • Если два или более приложений хотят использовать один и тот же идентификатор пользователя (чтобы иметь возможность обмениваться данными и т. д.), они должны быть подписаны одним и тем же ключом.
  2. Пакеты обновлений OTA должны быть подписаны одним из ключей, ожидаемых системой, иначе процесс установки их отклонит.

Отпустите клавиши

В дереве Android тестовые ключи находятся в папке 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 принимает на вход архив .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 RecoverySystem проверяет подпись по открытым ключам, хранящимся в основной системе в файле /system/etc/security/otacerts.zip (по умолчанию). Служба восстановления проверяет подпись по открытым ключам, хранящимся на RAM-диске раздела восстановления в файле /res/keys .

По умолчанию создаваемый сборкой архив .zip устанавливает 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. Закрытый ключ должен храниться в секрете и необходим для подписания пакета. Сам ключ может быть защищён паролем. Сертификат же содержит только открытую часть ключа, поэтому его можно распространять широко. Он используется для проверки того, что пакет подписан соответствующим закрытым ключом.

Стандартная сборка Android использует пять ключей, все из которых находятся в build/target/product/security :

тестовый ключ
Общий ключ по умолчанию для пакетов, для которых ключ не указан.
платформа
Тестовый ключ для пакетов, входящих в состав основной платформы.
поделился
Тестовый ключ для вещей, которые являются общими в процессе «Домой/Контакты».
СМИ
Тестовый ключ для пакетов, входящих в систему мультимедиа/загрузки.
сетевой стек
Тестовый ключ для пакетов, входящих в состав сетевой системы. Ключ networkstack используется для подписи двоичных файлов, разработанных как компоненты модульной системы . Если обновления модулей собираются отдельно и интегрируются в образ устройства как готовые сборки, вам может не потребоваться генерировать ключ networkstack в исходном дереве Android.

Отдельные пакеты указывают один из этих ключей, устанавливая 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. Сборка может использовать только закрытые ключи, не защищённые паролем.

Расширенные возможности подписи

Замена ключа подписи APK

Скрипт подписи sign_target_files_apks работает с целевыми файлами, сгенерированными для сборки. Вся информация о сертификатах и ​​закрытых ключах, используемых во время сборки, включена в целевые файлы. При запуске скрипта подписи для подписи перед выпуском ключи подписи можно заменить на основе имени ключа или имени 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 предположим, что у вас есть следующая ключевая конфигурация для файлов APEX com.android.conscrypt.apex , com.android.media.apex и com.android.runtime.release.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 переписывает описание сборки и цифровой отпечаток в файлах свойств сборки, чтобы отразить, что сборка подписана. Флаг --tag_changes управляет изменениями, которые вносятся в цифровой отпечаток. Запустите скрипт с -h , чтобы просмотреть документацию по всем флагам.

Генерация ключей вручную

Android использует 2048-битные ключи RSA с открытым показателем 3. Вы можете сгенерировать пары сертификат/закрытый ключ с помощью инструмента openssl с 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 без пароля, подходящий для использования с системой сборки. Чтобы создать файл .pk8, защищённый паролем (что следует делать для всех ключей выпуска), замените аргумент -nocrypt на -passout stdin ; тогда openssl зашифрует закрытый ключ паролем, считанным со стандартного ввода. Запрос не выводится, поэтому, если stdin — это терминал, программа будет выглядеть зависшей, хотя на самом деле она просто ждёт ввода пароля. Для аргумента -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