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

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

  1. Каждый файл .apk внутри образа должен быть подписан. Менеджер пакетов Android использует подпись .apk двумя способами:
    • When an application is replaced, it must be signed by the same key as the old application in order to get access to the old application's data. This holds true both for updating user apps by overwriting the .apk , and for overriding a system app with a newer version installed under /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 should be changed to reflect your organization's information. You can use any directory, but be careful to pick a location that is backed up and secure. Some vendors choose to encrypt their private key with a strong passphrase and store the encrypted key in source control; others store their release keys somewhere else entirely, such as on an air-gapped computer.

Для создания образа релиза используйте:

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

The sign_target_files_apks script takes a target-files .zip as input and produces a new target-files .zip in which all the .apk files have been signed with new keys. The newly signed images can be found under IMAGES/ in signed-target_files.zip .

Подпишите пакеты OTA

Подписанный ZIP-архив с целевыми файлами можно преобразовать в подписанный ZIP-архив с OTA-обновлением, используя следующую процедуру:
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

Подписи и боковая загрузка

Sideloading does not bypass recovery's normal package signature verification mechanism—before installing a package, recovery will verify that it is signed with one of the private keys matching the public keys stored in the recovery partition, just as it would for a package delivered over-the-air.

Пакеты обновлений, полученные от основной системы, обычно проверяются дважды: сначала основной системой с помощью метода RecoverySystem.verifyPackage() из Android API, а затем снова системой восстановления. API RecoverySystem проверяет подпись по открытым ключам, хранящимся в основной системе в файле /system/etc/security/otacerts.zip (по умолчанию). Система восстановления проверяет подпись по открытым ключам, хранящимся на диске оперативной памяти раздела восстановления в файле /res/keys .

By default, the target-files .zip produced by the build sets the OTA certificate to match the test key. On a released image, a different certificate must be used so that devices can verify the authenticity of the update package. Passing the -o flag to sign_target_files_apks , as shown in the previous section, replaces the test key certificate with the release key certificate from your certs directory.

Обычно образ системы и образ восстановления хранят один и тот же набор открытых ключей OTA. Добавив ключ только к набору ключей восстановления, можно подписывать пакеты, которые можно установить только путем боковой загрузки (при условии, что механизм загрузки обновлений основной системы корректно проверяет otacerts.zip). Вы можете указать дополнительные ключи, которые будут включены только в образ восстановления, установив переменную PRODUCT_EXTRA_RECOVERY_KEYS в определении продукта:

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

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

This includes the public key vendor/yoyodyne/security/tardis/sideload.x509.pem in the recovery keys file so it can install packages signed with it. The extra key is not included in otacerts.zip though, so systems that correctly verify downloaded packages do not invoke recovery for packages signed with this key.

Сертификаты и закрытые ключи

Каждый ключ состоит из двух файлов: сертификата с расширением .x509.pem и закрытого ключа с расширением .pk8. Закрытый ключ должен храниться в секрете и необходим для подписи пакета. Сам ключ может быть защищен паролем. Сертификат, напротив, содержит только открытую часть ключа, поэтому его можно широко распространять. Он используется для проверки того, что пакет был подписан соответствующим закрытым ключом.

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

тестовый ключ
Универсальный ключ по умолчанию для пакетов, в которых ключ не указан иным образом.
платформа
Тестовый ключ для пакетов, входящих в состав основной платформы.
общий
Тестовые ключи для элементов, используемых в процессе обмена данными между главной страницей и контактами.
СМИ
Тестовый ключ для пакетов, входящих в систему загрузки медиафайлов.
сетевой стек
Test key for packages that are part of the networking system. The networkstack key is used to sign binaries designed as Mainline modules . If your module updates are built separately and integrated as prebuilts in your device image, you may not need to generate a networkstack key in the Android source tree.

Individual packages specify one of these keys by setting LOCAL_CERTIFICATE in their Android.mk file. (testkey is used if this variable isn't set.) You can also specify an entirely different key by pathname, for example:

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

LOCAL_CERTIFICATE := device/yoyodyne/security/special

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

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

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

The signing script sign_target_files_apks works on the target files generated for a build. All the information on certificates and private keys used at build time is included in the target files. When running the signing script to sign for release, signing keys can be replaced based on key name or APK name.

Use the --key_mapping and --default_key_mappings flags to specify key replacement based on key names:

  • Флаг --key_mapping src_key = dest_key указывает на необходимость замены только одного ключа за раз.
  • The --default_key_mappings dir flag specifies a directory with five keys to replace all the keys in build/target/product/security ; it's equivalent to using --key_mapping five times to specify the mappings.
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-файлы как предварительно подписанные.

For the hypothetical tardis product, you need six password-protected keys: five to replace the five in build/target/product/security , and one to replace the additional key device/yoyodyne/security/special required by SpecialApp in the example above. If the keys were in the following files:

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 introduces the APEX file format for installing lower-level system modules. As explained in APEX signing , each APEX file is signed with two keys: one for the mini file system image within an APEX and the other for the entire APEX.

When signing for release, the two signing keys for an APEX file are replaced with release keys. The file system payload key is specified with the --extra_apex_payload flag and the entire APEX file signing key is specified with the --extra_apks flag.

Для продукта 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)
        [...]

Другие варианты

The sign_target_files_apks signing script rewrites the build description and fingerprint in the build properties files to reflect that the build is a signed build. The --tag_changes flag controls what edits are made to the fingerprint. Run the script with -h to see documentation on all flags.

Manually generate keys

В 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

The openssl pkcs8 command given above creates a .pk8 file with no password, suitable for use with the build system. To create a .pk8 secured with a password (which you should do for all actual release keys), replace the -nocrypt argument with -passout stdin ; then openssl will encrypt the private key with a password read from standard input. No prompt is printed, so if stdin is the terminal the program will appear to hang when it's really just waiting for you to enter a password. Other values can be used for the-passout argument to read the password from other locations; for details, see the openssl documentation .

Промежуточный файл temp.pem содержит закрытый ключ без какой-либо защиты паролем, поэтому его следует удалять с осторожностью при генерации ключей для выпуска. В частности, утилита GNUshred может быть неэффективна на сетевых или журналируемых файловых системах. При генерации ключей можно использовать рабочий каталог, расположенный на виртуальном диске в оперативной памяти (например, раздел tmpfs), чтобы гарантировать, что промежуточные файлы не будут случайно раскрыты.

Создание файлов изображений

When you have signed-target_files.zip , you need to create the image so you can put it onto a device. To create the signed image from the target files, run the following command from the root of the Android tree:

img_from_target_files signed-target_files.zip signed-img.zip
В результате будет получен файл signed-img.zip , содержащий все файлы .img . Для загрузки образа на устройство используйте fastboot следующим образом:
fastboot update signed-img.zip