Образы ОС Android используют криптографические подписи в двух местах:
- Каждый файл
.apk
внутри образа должен быть подписан. Менеджер пакетов Android использует подпись.apk
двумя способами:- При замене приложения его необходимо подписать тем же ключом, что и старое приложение, чтобы получить доступ к данным старого приложения. Это справедливо как для обновления пользовательских приложений путем перезаписи
.apk
, так и для переопределения системного приложения более новой версией, установленной в/data
. - Если два или более приложений хотят использовать общий идентификатор пользователя (чтобы они могли обмениваться данными и т. д.), они должны быть подписаны одним и тем же ключом.
- При замене приложения его необходимо подписать тем же ключом, что и старое приложение, чтобы получить доступ к данным старого приложения. Это справедливо как для обновления пользовательских приложений путем перезаписи
- Пакеты 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
принимает .zip
целевых файлов в качестве входных данных и создает новый .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
.
По умолчанию .zip
-файл целевых файлов, созданный в результате сборки, устанавливает сертификат 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
:
- тестовый ключ
- Общий ключ по умолчанию для пакетов, в которых ключ не указан иначе.
- платформа
- Тестовый ключ для пакетов, которые являются частью базовой платформы.
- общий
- Тестовый ключ для вещей, которые доступны в процессе «Домой/Контакты».
- СМИ
- Тестовый ключ для пакетов, которые являются частью системы мультимедиа/загрузки.
Отдельные пакеты указывают один из этих ключей, устанавливая 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 keyopenssl 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 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 зашифрует закрытый ключ паролем, считанным со стандартного ввода. Приглашение не выводится, поэтому, если стандартный ввод является терминалом, программа будет зависать, хотя на самом деле она просто ожидает ввода пароля. Другие значения можно использовать для аргумента-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