Code Signing (podpisywanie kodu) — mechanizm cyfrowego podpisywania plików wykonywalnych, gwarantujący autentyczność programisty i integralność aplikacji. W Androidzie każdy plik APK musi być podpisany certyfikatem przed instalacją na urządzeniu lub publikacją w Google Play. Według danych Google, 2024, Android obsługuje cztery generacje schematów podpisu: od v1 opartego na JAR do v4 dla instalacji strumieniowej.
Najważniejsze
Code Signing — proces kryptograficzny, podczas którego programista podpisuje wykonywalny kod swoim cyfrowym certyfikatem. Podpis jest tworzony przy użyciu szyfrowania asymetrycznego: kluczem prywatnym programisty generowany jest cyfrowy podpis, a klucz publiczny jest osadzany w certyfikacie. Każdy może zweryfikować podpis za pomocą klucza publicznego, ale zmiana kodu bez naruszenia podpisu jest niemożliwa.
W tworzeniu aplikacji mobilnych podpisywanie kodu pełni trzy funkcje. Pierwsza — uwierzytelnianie: użytkownik i platforma mogą zidentyfikować programistę aplikacji. Druga — integralność: każda zmiana APK po podpisaniu unieważnia podpis. Trzecia — zaufana aktualizacja: platforma pozwala aktualizować aplikację tylko tymi APK, które są podpisane tym samym certyfikatem co zainstalowana wersja.
Cyfrowy podpis aplikacji Android ma znaczenie prawne. Zgodnie z ustawodawstwem Federacji Rosyjskiej (63-FZ) i europejskim eIDAS, kwalifikowany podpis elektroniczny jest zrównany z własnoręcznym. Jednak podpis APK przy użyciu certyfikatu samopodpisanego (typowa praktyka w Androidzie) nie jest kwalifikowany — potwierdza integralność, ale nie tożsamość programisty z prawnego punktu widzenia.
Android obsługuje cztery schematy podpisu APK, z których każdy rozwiązuje problemy poprzedniej wersji i dodaje nowe możliwości. Wszystkie schematy mogą współistnieć w jednym APK — jest to konieczne dla zachowania wstecznej kompatybilności ze starszymi wersjami Androida.
Schemat v1 (JAR signing) pojawił się w Android 1.0. Podpisuje on poszczególne pliki wewnątrz archiwum APK za pomocą wpisów w META-INF/MANIFEST.MF. Wadą jest: można zmienić APK (dodać lub usunąć pliki) i podpisać ponownie tylko zmienione, nie naruszając podpisu pozostałych. To czyni v1 podatnym na niektóre ataki. Schemat v2 (APK Signature Scheme), wprowadzony w Android 7.0, podpisuje cały plik APK w całości, włączając wszystkie bajty z wyjątkiem samego podpisu, co eliminuje możliwą selektywnej modyfikacji.
| Schemat | Android | Cecha | Rotacja klucza |
|---|---|---|---|
| v1 (JAR) | 1.0+ | Podpis każdego pliku | Nie |
| v2 | 7.0+ | Podpis całego APK | Nie |
| v3 | 9.0+ | Podpis + rotacja | Tak |
| v4 | 11.0+ | Strumieniowy + ADB | Tak |
Schemat v3, przedstawiony w Android 9.0, rozwiązuje odwieczny problem: co zrobić, gdy klucz podpisu został skompromitowany lub wygasł? Wcześniej zmiana klucza podpisu oznaczała, że aplikacja jest postrzegana jako nowa — nie można jej zainstalować na istniejącej. v3 dodaje mechanizm rotacji: do APK można dołączyć dowód zmiany klucza (proof-of-rotation), podpisany starym kluczem. System sprawdza łańcuch i pozwala zaktualizować aplikację podpisaną nowym kluczem.
Keystore — chroniony kontener zawierający klucze prywatne i certyfikaty do podpisywania aplikacji. W tworzeniu aplikacji Android używany jest format JKS (Java KeyStore) lub PKCS12. Keystore jest tworzony za pomocą narzędzia keytool, wchodzącego w skład JDK. Każdy klucz w magazynie jest identyfikowany pseudonimem (alias) i chroniony hasłem.
Certyfikat w keystore zawiera klucz publiczny i informacje o właścicielu: nazwę organizacji, kraj, okres ważności. W przypadku aplikacji Android certyfikat może być samopodpisany — Google nie wymaga korzystania z centrum certyfikacji (CA), co odróżnia androida od iOS. Jednak okres ważności certyfikatu musi wynosić co najmniej 25 lat, ponieważ aplikacja będzie aktualizowana tym samym kluczem.
# Tworzenie nowego keystore do podpisu
keytool -genkey -v -keystore my-release.keystore \
-alias my-app-alias \
-keyalg RSA \
-keysize 2048 \
-validity 10000
# Wyswietlanie zawartosci keystore
keytool -list -v -keystore my-release.keystore
Android obsługuje dwa algorytmy dla kluczy podpisu: RSA i ECDSA. RSA z rozmiarem klucza 2048 bitów — standard de facto, obsługiwany przez wszystkie wersje Androida. ECDSA (Elliptic Curve Digital Signature Algorithm) z krzywą P-256 zapewnia taką samą siłę kryptograficzną przy mniejszym rozmiarze klucza. Od Androida 9.0 zaleca się używanie ECDSA, ponieważ jest szybszy w weryfikacji na urządzeniach mobilnych.
W Android Gradle Plugin podpis jest konfigurowany przez blok signingConfigs w build.gradle poziomu modułu. Dla kompilacji debug Android Studio automatycznie tworzy debugowy keystore ze znanymi hasłami. Dla kompilacji release programista podaje ścieżkę do swojego keystore, pseudonim klucza i hasła. Zaleca się przechowywanie haseł w oddzielnych plikach konfiguracyjnych, wykluczonych z systemu kontroli wersji.
Nowoczesna praktyka to scentralizowane zarządzanie podpisem przez CI/CD. Jenkins, GitLab CI lub GitHub Actions mogą przechowywać keystore jako chroniony artefakt, a hasła — w sekretach środowiska. Zapobiega to wyciekowi kluczy przez repozytorium i upraszcza zmianę klucza w razie potrzeby.
// build.gradle (poziom modulu) — konfiguracja podpisu
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
}
}
}
Dla maksymalnej kompatybilności APK powinien być podpisany wszystkimi trzema schematami (v1 + v2 + v3). Android Gradle Plugin domyślnie włącza wszystkie schematy. APK podpisany tylko v2 nie zainstaluje się na Android 6.0 i starszych. APK tylko z v1 nie otrzyma zalet integralności v2 na Android 7.0+. Włączenie wszystkich schematów nie zwiększa rozmiaru APK o więcej niż 1–2% i zapewnia kompatybilność z każdym urządzeniem.
Play App Signing — usługa Google Play, która centralnie zarządza kluczami podpisu aplikacji. Programista przesyła do Google Play Console APK podpisany kluczem przesyłania (upload key), a Google Play podpisuje go ponownie kluczem dystrybucji (distribution key) przed dostarczeniem użytkownikom. Chroni to klucz dystrybucji przed utratą lub kompromitacją.
Zalety Play App Signing: bezpieczeństwo — klucz dystrybucji jest przechowywany w chronionym magazynie Google; rotacja — można poprosić o zmianę klucza przez konsolę; odzyskiwanie — w razie utraty klucza przesyłania można wygenerować nowy. Wadą jest: dla aplikacji istniejących przed wdrożeniem Play App Signing, przejście wymaga utworzenia nowej aplikacji, ponieważ stary klucz dystrybucji jest już używany.
# Uzyskiwanie odcisku certyfikatu (SHA-256)
keytool -list -v -keystore my-release.keystore \
-alias my-app-alias | grep "SHA256"
# Sprawdzanie podpisu APK przez apksigner
apksigner verify --verbose app-release.apk
Jeśli klucz podpisu został utracony, a Play App Signing nie jest używane, odzyskanie możliwości aktualizacji aplikacji jest niemożliwe — trzeba będzie utworzyć nową aplikację z nową nazwą pakietu. Jest to jeden z głównych powodów, aby używać Play App Signing. Google zaleca przechowywanie kopii zapasowej keystore w chronionym magazynie offline (zaszyfrowany nośnik USB, skrytka bankowa).
Podczas instalacji APK Android wykonuje weryfikację podpisu w kilku etapach. Pierwszy — sprawdzenie certyfikatu: czy nie wygasł, czy format jest poprawny. Drugi — sprawdzenie podpisu: czy podpis kryptograficzny zgadza się z zawartością APK. Trzeci — porównanie certyfikatu z zainstalowaną wersją: jeśli aplikacja już istnieje na urządzeniu, certyfikat musi być zgodny, w przeciwnym razie instalacja jest blokowana.
System weryfikacji jest wbudowany w PackageManagerService. Podczas przetwarzania żądania instalacji PMS wyodrębnia podpis z APK, sprawdza go za pomocą klasy android.util.PackageParser i porównuje z zapisanym podpisem zainstalowanej aplikacji (jeśli istnieje). W przypadku niezgodności użytkownik otrzymuje błąd „INSTALL_FAILED_UPDATE_INCOMPATIBLE”. Ten mechanizm zapobiega atakom podstawienia (złośliwe oprogramowanie nie może zaktualizować legalnej aplikacji swoją wersją).
Programista może samodzielnie sprawdzić podpis APK za pomocą narzędzia apksigner z Android SDK Build Tools. Polecenie apksigner verify --verbose app.apk pokazuje, jakimi schematami APK jest podpisany, czy certyfikaty są ważne i czy podpisy zgadzają się z zawartością. Do programowego sprawdzania podpisu zainstalowanej aplikacji używa się PackageManager.getPackageInfo() z flagą GET_SIGNATURES.
// Programowe sprawdzanie podpisu zainstalowanej aplikacji
fun getAppSignature(context: Context, packageName: String): String? {
val pm = context.packageManager
val info = pm.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
)
return info.signatures?.firstOrNull()?.toCharsString()
}
Bezpieczeństwo klucza podpisu — krytyczny aspekt tworzenia aplikacji Android. Kompromitacja klucza pozwala osobie atakującej podpisywać aktualizacje twojej aplikacji własnym kodem. Podstawowe zasady: nigdy nie przechowuj klucza w repozytorium, nie używaj jednego klucza do różnych aplikacji, nie przesyłaj klucza przez niezabezpieczone kanały (email, komunikatory).
Zalecana praktyka to rozdzielenie kluczy. Używaj oddzielnego klucza dla każdej aplikacji i oddzielnego klucza do przesyłania do Google Play (upload key). Dla kompilacji debug Android Studio tworzy wspólny debug.keystore — nie można go używać do kompilacji release. Okres ważności certyfikatu powinien wynosić 25–30 lat (obowiązujący standard, potwierdzony przez Google).
| Praktyka | Zalecenie |
|---|---|
| Przechowywanie klucza | Zaszyfrowany nośnik, CI/CD secrets |
| Okres certyfikatu | Co najmniej 25 lat |
| Algorytm | RSA 2048+ lub ECDSA P-256 |
| Rozdzielenie | Oddzielny klucz na aplikację |
| Rezerwowanie | Kopia offline keystore |
Regularnie sprawdzaj integralność łańcucha podpisu. Przy zmianie pracowników mających dostęp do kluczy, zaktualizuj upload key przez Google Play Console. Używaj narzędzi takich jak Google Play Integrity API do sprawdzenia, czy twoja aplikacja nie została sfałszowana na urządzeniach użytkowników. API zwraca dane o podpisie, integralności i wysyła je na serwer do weryfikacji.
Często zadawane pytania
Code Signing — to cyfrowy podpis pliku APK, który potwierdza, że aplikacja została utworzona przez konkretnego programistę i nie została zmodyfikowana po podpisaniu. Bez podpisu APK nie zainstaluje się na urządzeniu.
Użyj narzędzia keytool z JDK: keytool -genkey -v -keystore my-release.keystore -alias my-alias -keyalg RSA -keysize 2048 -validity 10000. Otrzymany keystore wskaż w build.gradle w bloku signingConfigs.
Jeśli klucz został utracony i nie używasz Play App Signing, aktualizacja aplikacji stanie się niemożliwa. Będziesz musiał utworzyć nową aplikację w Google Play z nową nazwą pakietu. Używaj Play App Signing, aby zabezpieczyć się przed utratą klucza.
v1 podpisuje każdy plik wewnątrz APK osobno — osoba atakująca może zmienić jeden plik i podpisać ponownie tylko go. v2 podpisuje cały APK w całości — każda zmiana unieważnia podpis, co zapewnia wyższy poziom bezpieczeństwa.
Play App Signing — usługa Google Play, która centralnie przechowuje klucz dystrybucji aplikacji. Programista przesyła APK podpisany upload key, a Google podpisuje go ponownie przed dostarczeniem użytkownikom, chroniąc klucz przed utratą lub kradzieżą.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również