Подписание сборок для выпуска

Образы ОС 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 принимает в качестве входных данных файл 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

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

Неопубликованная загрузка не обходит обычный механизм проверки подписи пакета восстановления — перед установкой пакета восстановление проверяет, подписан ли он одним из закрытых ключей, совпадающих с открытыми ключами, хранящимися в разделе восстановления, точно так же, как это было бы для пакета, доставленного поверх -воздуха.

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

По умолчанию .zip -файл target-files, созданный сборкой, устанавливает сертификат OTA в соответствии с тестовым ключом. В выпущенном образе необходимо использовать другой сертификат, чтобы устройства могли проверить подлинность пакета обновления. Передача флага -o в sign_target_files_apks , как показано в предыдущем разделе, заменяет сертификат тестового ключа сертификатом ключа выпуска из каталога сертификатов.

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

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

После того, как вы подписали 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