В образах ОС Android криптографические подписи используются в двух местах:
- Каждый файл
.apkв образе должен быть подписан. Менеджер пакетов Android использует подпись.apkдвумя способами:- Чтобы новое приложение получило доступ к данным старого, оно должно быть подписано тем же ключом, что и старое. Это относится как к обновлению пользовательских приложений путем перезаписи
.apk, так и к замене системного приложения более новой версией, установленной в/data. - Если два или более приложений должны использовать один и тот же идентификатор пользователя (например, чтобы обмениваться данными), они должны быть подписаны одним и тем же ключом.
- Чтобы новое приложение получило доступ к данным старого, оно должно быть подписано тем же ключом, что и старое. Это относится как к обновлению пользовательских приложений путем перезаписи
- Пакеты беспроводных обновлений должны быть подписаны одним из ключей, ожидаемых системой, иначе процесс установки будет отклонен.
Отпустите клавиши.
Дерево 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-certsfor x in releasekey platform shared media networkstack; do \ ./development/tools/make_key ~/.android-certs/$x "$subject"; \ done
$subject следует изменить, чтобы отразить информацию о вашей организации. Вы можете выбрать любой каталог, но убедитесь, что он защищен и его содержимое копируется в облако. Некоторые поставщики шифруют закрытый ключ надежной кодовой фразой и хранят зашифрованный ключ в системе управления версиями, а другие хранят ключи выпуска в другом месте, например на компьютере без доступа к интернету.
Чтобы сгенерировать изображение для релиза, используйте:
make distsign_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
- Стандартный ключ для пакетов, в которых не указан другой ключ.
- платформа
- Тестовый ключ для пакетов, входящих в основную платформу.
- общий
- Тестовый ключ для объектов, которыми можно делиться в процессе настройки дома или контактов.
- медиа
- Тестовый ключ для пакетов, которые являются частью системы медиаконтента или скачивания.
Отдельные пакеты указывают один из этих ключей, задавая 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 keyopenssl genrsa -3 -out temp.pem 2048Generating RSA private key, 2048 bit long modulus ....+++ .....................+++ e is 3 (0x3) # create a certificate with the public part of the keyopenssl 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 keyopenssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt# securely delete the temp.pem fileshred --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