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 автоматично създава debug 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". Този механизъм предотвратява атаки на подмяна (зловреден софтуер не може да актуализира легитимно приложение със своя версия).
Разработчикът може самостоятелно да провери подписа на 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 разработката. Компрометирането на ключа позволява на нападател да подписва актуализациите на вашето приложение със своя код. Основни правила: никога не съхранявайте ключа в хранилището, не използвайте един и същ ключ за различни приложения, не предавайте ключа чрез незащитени канали (имейл, месинджъри).
Препоръчителната практика е разделяне на ключове. Използвайте отделен ключ за всяко приложение и отделен ключ за качване в Google Play (upload key). За debug компилации 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също