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

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

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

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

Дерево 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 принимает в качестве входных данных целевые файлы .zip и создает новые целевые файлы .zip, в которых все файлы .apk подписаны новыми ключами. Новые подписанные изображения можно найти в разделе IMAGES/ в signed-target_files.zip.

Как подписывать пакеты OTA

Подписанный ZIP-архив с целевыми файлами можно преобразовать в подписанный ZIP-архив с беспроводным обновлением, выполнив следующие действия:
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, созданных сборкой, сертификат OTA соответствует тестовому ключу. Для выпущенного образа необходимо использовать другой сертификат, чтобы устройства могли проверить подлинность пакета обновления. Передача флага -o в sign_target_files_apks, как показано в предыдущем разделе, заменяет сертификат тестового ключа сертификатом ключа выпуска из каталога certs.

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

testkey
Стандартный ключ для пакетов, в которых не указан другой ключ.
платформа
Тестовый ключ для пакетов, входящих в основную платформу.
общий
Тестовый ключ для объектов, которыми можно делиться в процессе настройки дома или контактов.
медиа
Тестовый ключ для пакетов, которые являются частью системы медиаконтента или скачивания.
networkstack
Тестовый ключ для пакетов, которые являются частью сетевой системы. Ключ 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 может быть неэффективна в сетевых или журналируемых файловых системах. Чтобы избежать случайного раскрытия промежуточных ключей, при их создании можно использовать рабочую папку, расположенную на 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