Code Signing (подпись кода) — механизм цифровой подписи исполняемых файлов, гарантирующий подлинность разработчика и целостность приложения. В Android каждый APK-файл обязательно подписывается сертификатом перед установкой на устройство или публикацией в Google Play. По данным Google, 2024, Android поддерживает четыре поколения схем подписи: от v1 на основе JAR до v4 для потоковой установки.
Главное
Code Signing — криптографический процесс, в ходе которого разработчик подписывает исполняемый код своим цифровым сертификатом. Подпись создаётся с использованием асимметричного шифрования: закрытым ключом разработчика генерируется цифровая подпись, а открытый ключ встраивается в сертификат. Любой может проверить подпись, используя открытый ключ, но изменить код без нарушения подписи невозможно.
В мобильной разработке подпись кода выполняет три функции. Первая — аутентификация: пользователь и платформа могут идентифицировать разработчика приложения. Вторая — целостность: любое изменение APK после подписания делает подпись недействительной. Третья — доверенное обновление: платформа позволяет обновлять приложение только теми APK, которые подписаны тем же сертификатом, что и установленная версия.
Цифровая подпись Android-приложений имеет юридическую значимость. В соответствии с законодательством РФ (63-ФЗ) и европейским eIDAS, квалифицированная электронная подпись приравнивается к собственноручной. Однако подпись APK с использованием самоподписанного сертификата (обычная практика в Android) не является квалифицированной — она подтверждает целостность, но не личность разработчика с юридической точки зрения.
Android поддерживает четыре схемы подписи APK, каждая из которых решает проблемы предыдущей версии и добавляет новые возможности. Все схемы могут сосуществовать в одном APK — это необходимо для обратной совместимости со старыми версиями Android.
Схема v1 (JAR signing) появилась в Android 1.0. Она подписывает отдельные файлы внутри APK-архива с помощью записей в META-INF/MANIFEST.MF. Недостаток: можно изменить APK (добавить или удалить файлы) и переподписать только изменённые, не трогая подпись остальных. Это делает v1 уязвимой для некоторых атак. Схема v2 (APK Signature Scheme), введённая в Android 7.0, подписывает весь APK-файл целиком, включая все байты кроме самой подписи, что исключает возможность выборочной модификации.
| Схема | Android | Особенность | Ротация ключа |
|---|---|---|---|
| v1 (JAR) | 1.0+ | Подпись каждого файла | Нет |
| v2 | 7.0+ | Подпись всего APK | Нет |
| v3 | 9.0+ | Подпись + ротация | Да |
| v4 | 11.0+ | Потоковая + ADB | Да |
Схема v3, представленная в Android 9.0, решает давнюю проблему: что делать, если ключ подписи скомпрометирован или истёк? Ранее смена ключа подписи означала, что приложение воспринимается как новое — его нельзя установить поверх существующего. v3 добавляет механизм ротации: в APK можно включить доказательство смены ключа (proof-of-rotation), подписанное старым ключом. Система проверяет цепочку и позволяет обновить приложение, подписанное новым ключом.
Keystore — защищённый контейнер, содержащий закрытые ключи и сертификаты для подписи приложений. В Android-разработке используется формат JKS (Java KeyStore) или PKCS12. Keystore создаётся утилитой keytool, входящей в состав JDK. Каждый ключ в хранилище идентифицируется псевдонимом (alias) и защищён паролем.
Сертификат в keystore содержит открытый ключ и информацию о владельце: имя организации, страну, срок действия. Для Android-приложений сертификат может быть самоподписанным — Google не требует обращения в центр сертификации (CA), что отличает Android от iOS. Однако срок действия сертификата должен быть не менее 25 лет, так как приложение будет обновляться этим же ключом.
# Создание нового keystore для подписи
keytool -genkey -v -keystore my-release.keystore \
-alias my-app-alias \
-keyalg RSA \
-keysize 2048 \
-validity 10000
# Просмотр содержимого keystore
keytool -list -v -keystore my-release.keystore
Android поддерживает два алгоритма для ключей подписи: RSA и ECDSA. RSA с размером ключа 2048 бит — стандарт де-факто, поддерживаемый всеми версиями Android. ECDSA (Elliptic Curve Digital Signature Algorithm) с кривой P-256 обеспечивает такую же криптостойкость при меньшем размере ключа. Начиная с Android 9.0 рекомендуется использовать ECDSA, так как он быстрее в верификации на мобильных устройствах.
В Android Gradle Plugin подпись настраивается через блок signingConfigs в build.gradle уровня модуля. Для debug-сборок Android Studio автоматически создаёт отладочный keystore с известными паролями. Для release-сборок разработчик указывает путь к своему keystore, псевдоним ключа и пароли. Рекомендуется хранить пароли в отдельных конфигурационных файлах, исключённых из системы контроля версий.
Современная практика — централизованное управление подписью через CI/CD. Jenkins, GitLab CI или GitHub Actions могут хранить keystore как защищённый артефакт, а пароли — в секретах окружения. Это предотвращает утечку ключей через репозиторий и упрощает смену ключа при необходимости.
// build.gradle (app-level) — конфигурация подписи
android {
signingConfigs {
release {
storeFile file("my-release.keystore")
storePassword System.getenv("KEYSTORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
Для максимальной совместимости APK должен быть подписан всеми тремя схемами (v1 + v2 + v3). Android Gradle Plugin по умолчанию включает все схемы. APK, подписанный только v2, не установится на Android 6.0 и ниже. APK только с v1 не получит преимущества целостности v2 на Android 7.0+. Включение всех схем не увеличивает размер APK более чем на 1–2% и обеспечивает совместимость с любым устройством.
Play App Signing — сервис Google Play, который централизованно управляет ключами подписи приложений. Разработчик загружает в Google Play Console APK, подписанный ключом загрузки (upload key), а Google Play переподписывает его ключом распространения (distribution key) перед доставкой пользователям. Это защищает ключ распространения от потери или компрометации.
Преимущества Play App Signing: безопасность — ключ распространения хранится в защищённом хранилище Google; ротация — можно запросить смену ключа через консоль; восстановление — при потере ключа загрузки можно сгенерировать новый. Недостаток: для приложений, существовавших до внедрения Play App Signing, переход требует создания нового приложения, так как старый ключ распространения уже используется.
# Получение отпечатка сертификата (SHA-256)
keytool -list -v -keystore my-release.keystore \
-alias my-app-alias | grep "SHA256"
# Проверка подписи APK через apksigner
apksigner verify --verbose app-release.apk
Если ключ подписи утерян, а Play App Signing не используется, восстановить возможность обновления приложения невозможно — придётся создавать новое приложение с новым пакетным именем. Это одна из главных причин использовать Play App Signing. Google рекомендует хранить резервную копию keystore в защищённом офлайн-хранилище (зашифрованный USB-носитель, банковская ячейка).
При установке APK Android выполняет верификацию подписи в несколько этапов. Первый — проверка сертификата: не истёк ли срок действия, корректен ли формат. Второй — проверка подписи: совпадает ли криптографическая подпись с содержимым APK. Третий — сравнение сертификата с установленной версией: если приложение уже есть на устройстве, сертификат должен совпадать, иначе установка блокируется.
Система верификации встроена в PackageManagerService. При обработке запроса на установку PMS извлекает подпись из APK, проверяет её с помощью класса android.util.PackageParser и сравнивает с сохранённой подписью установленного приложения (если оно существует). При несовпадении пользователь получает ошибку "INSTALL_FAILED_UPDATE_INCOMPATIBLE". Этот механизм предотвращает атаки подмены (malware не может обновить легитимное приложение своей версией).
Разработчик может самостоятельно проверить подпись APK с помощью утилиты apksigner из Android SDK Build Tools. Команда apksigner verify --verbose app.apk показывает, какими схемами подписан APK, действительны ли сертификаты и совпадают ли подписи с содержимым. Для программной проверки подписи установленного приложения используется PackageManager.getPackageInfo() с флагом GET_SIGNATURES.
// Программная проверка подписи установленного приложения
fun getAppSignature(context: Context, packageName: String): String? {
val pm = context.packageManager
val info = pm.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
)
return info.signatures?.firstOrNull()?.toCharsString()
}
Безопасность ключа подписи — критический аспект Android-разработки. Компрометация ключа позволяет злоумышленнику подписывать обновления вашего приложения своим кодом. Основные правила: никогда не храните ключ в репозитории, не используйте один ключ для разных приложений, не передавайте ключ через незащищённые каналы (email, мессенджеры).
Рекомендуемая практика — разделение ключей. Используйте отдельный ключ для каждого приложения и отдельный ключ для загрузки в Google Play (upload key). Для отладочных сборок Android Studio создаёт общий debug.keystore — его нельзя использовать для release-сборок. Срок действия сертификата должен составлять 25–30 лет (действующий стандарт, подтверждённый Google).
| Практика | Рекомендация |
|---|---|
| Хранение ключа | Зашифрованный носитель, CI/CD secrets |
| Срок сертификата | Не менее 25 лет |
| Алгоритм | RSA 2048+ или ECDSA P-256 |
| Разделение | Отдельный ключ на приложение |
| Резервирование | Офлайн-копия keystore |
Регулярно проверяйте целостность цепочки подписи. При смене сотрудников, имеющих доступ к ключам, обновляйте upload key через Google Play Console. Используйте инструменты вроде Google Play Integrity API для проверки, что ваше приложение не было подделано на устройствах пользователей. API возвращает данные о подписи, целостности и отправляет их на сервер для верификации.
Часто задаваемые вопросы
Code Signing — это цифровая подпись APK-файла, которая подтверждает, что приложение создано конкретным разработчиком и не было изменено после подписания. Без подписи APK не установится на устройство.
Используйте утилиту keytool из JDK: keytool -genkey -v -keystore my-release.keystore -alias my-alias -keyalg RSA -keysize 2048 -validity 10000. Полученный keystore укажите в build.gradle в блоке signingConfigs.
Если ключ утерян и вы не используете Play App Signing, обновление приложения станет невозможным. Придётся создавать новое приложение в Google Play с новым package name. Используйте Play App Signing для защиты от потери ключа.
v1 подписывает каждый файл внутри APK по отдельности — злоумышленник может изменить один файл и переподписать только его. v2 подписывает весь APK целиком — любое изменение делает подпись недействительной, что обеспечивает более высокий уровень безопасности.
Play App Signing — сервис Google Play, который централизованно хранит ключ распространения приложений. Разработчик загружает APK, подписанный upload key, а Google переподписывает его перед доставкой пользователям, защищая ключ от потери или кражи.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также