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

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

По умолчанию в .zip target-files, созданном в процессе сборки, сертификат 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

Теперь для подписи файла SpecialApp.apk используется ключ device/yoyodyne/security/special.{x509.pem,pk8} Сборка может использовать только закрытые ключи, не защищенные паролем.

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

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