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

Образы ОС 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/[email protected]'
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-файл целевых файлов можно преобразовать в подписанный 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 .

По умолчанию target-files .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 используется для подписи двоичных файлов, разработанных как Modular System Components . Если обновления модулей собираются отдельно и интегрируются как предварительно собранные в образе устройства, вам может не потребоваться генерировать ключ 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/[email protected]'

# 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