Podpisz kompilacje do wydania

Obrazy systemu operacyjnego Android korzystają z podpisów kryptograficznych w dwóch miejscach:

  1. Każdy plik .apk znajdujący się w obrazie musi być podpisany. Menedżer pakietów Androida używa podpisu .apk na dwa sposoby:
    • W przypadku wymiany aplikacji, aby uzyskać dostęp do danych starej aplikacji, należy ją podpisać tym samym kluczem, co stara aplikacja. Dotyczy to zarówno aktualizacji aplikacji użytkownika poprzez zastąpienie .apk , jak i zastąpienia aplikacji systemowej nowszą wersją zainstalowaną w /data .
    • Jeśli dwie lub więcej aplikacji chce współdzielić identyfikator użytkownika (aby mogły udostępniać dane itp.), muszą być podpisane tym samym kluczem.
  2. Pakiety aktualizacji OTA muszą być podpisane jednym z kluczy oczekiwanych przez system, w przeciwnym razie proces instalacji je odrzuci.

Zwolnij klawisze

Drzewo Androida zawiera klucze testowe w build/target/product/security . Zbudowanie obrazu systemu operacyjnego Android za pomocą make spowoduje podpisanie wszystkich plików .apk przy użyciu kluczy testowych. Ponieważ klucze testowe są publicznie znane, każdy może podpisywać własne pliki .apk tymi samymi kluczami, co może pozwolić im na zastępowanie lub przejmowanie aplikacji systemowych wbudowanych w obraz systemu operacyjnego. Z tego powodu niezwykle ważne jest podpisanie każdego publicznie udostępnionego lub wdrożonego obrazu systemu operacyjnego Android specjalnym zestawem kluczy wersji , do których tylko Ty masz dostęp.

Aby wygenerować własny, unikalny zestaw kluczy wydania, uruchom następujące polecenia z katalogu głównego drzewa Androida:

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 należy zmienić, aby odzwierciedlał informacje o Twojej organizacji. Możesz użyć dowolnego katalogu, ale pamiętaj, aby wybrać lokalizację, która ma kopię zapasową i jest bezpieczna. Niektórzy dostawcy decydują się na szyfrowanie swojego klucza prywatnego silnym hasłem i przechowywanie zaszyfrowanego klucza w kontroli źródła; inni przechowują klucze zwalniające w zupełnie innym miejscu, na przykład na komputerze z otwartą przestrzenią powietrzną.

Aby wygenerować obraz wydania, użyj:

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 pobiera plik .zip plików docelowych jako dane wejściowe i tworzy nowy plik .zip plików docelowych, w którym wszystkie pliki .apk zostały podpisane nowymi kluczami. Nowo podpisane obrazy można znaleźć w zakładce IMAGES/ w signed-target_files.zip .

Podpisz pakiety OTA

Podpisany plik zip plików docelowych można przekonwertować na podpisany plik zip aktualizacji OTA, korzystając z następującej procedury:
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

Podpisy i ładowanie boczne

Ładowanie boczne nie omija normalnego mechanizmu weryfikacji podpisu pakietu podczas odzyskiwania — przed zainstalowaniem pakietu odzyskiwanie sprawdza, czy jest on podpisany jednym z kluczy prywatnych pasujących do kluczy publicznych przechowywanych na partycji odzyskiwania, tak samo jak w przypadku pakietu dostarczonego przez -powietrze.

Pakiety aktualizacji otrzymane z systemu głównego są zazwyczaj weryfikowane dwukrotnie: raz przez system główny przy użyciu metody RecoverySystem.verifyPackage() w interfejsie API systemu Android, a następnie ponownie poprzez odzyskiwanie. API RecoverySystem sprawdza podpis względem kluczy publicznych przechowywanych w systemie głównym, w pliku /system/etc/security/otacerts.zip (domyślnie). Odzyskiwanie sprawdza podpis względem kluczy publicznych przechowywanych na dysku RAM partycji odzyskiwania, w pliku /res/keys .

Domyślnie plik .zip plików docelowych utworzony przez kompilację ustawia certyfikat OTA tak, aby pasował do klucza testowego. Na udostępnionym obrazie należy użyć innego certyfikatu, aby urządzenia mogły zweryfikować autentyczność pakietu aktualizacji. Przekazanie flagi -o do sign_target_files_apks , jak pokazano w poprzedniej sekcji, zastępuje certyfikat klucza testowego certyfikatem klucza wydania z katalogu certyfikatów.

Zwykle obraz systemu i obraz odzyskiwania przechowują ten sam zestaw kluczy publicznych OTA. Dodając klucz tylko do zestawu kluczy odzyskiwania, możliwe jest podpisywanie pakietów, które można zainstalować tylko poprzez ładowanie boczne (zakładając, że mechanizm pobierania aktualizacji głównego systemu poprawnie przeprowadza weryfikację względem pliku otacerts.zip). Możesz określić dodatkowe klucze, które mają być uwzględniane tylko podczas odzyskiwania, ustawiając zmienną PRODUCT_EXTRA_RECOVERY_KEYS w definicji produktu:

vendor/yoyodyne/tardis/products/tardis.mk
 [...]

PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload

Obejmuje to vendor/yoyodyne/security/tardis/sideload.x509.pem w pliku kluczy odzyskiwania, aby mógł zainstalować podpisane nim pakiety. Dodatkowy klucz nie jest jednak zawarty w otacerts.zip, dlatego systemy poprawnie weryfikujące pobrane pakiety nie wywołują odzyskiwania pakietów podpisanych tym kluczem.

Certyfikaty i klucze prywatne

Każdy klucz występuje w dwóch plikach: certyfikat z rozszerzeniem .x509.pem i klucz prywatny z rozszerzeniem .pk8. Klucz prywatny należy zachować w tajemnicy i jest on niezbędny do podpisania przesyłki. Sam klucz może być chroniony hasłem. Certyfikat natomiast zawiera tylko publiczną połowę klucza, dzięki czemu może być szeroko rozpowszechniany. Służy do sprawdzenia, czy paczka została podpisana odpowiednim kluczem prywatnym.

Standardowa kompilacja Androida wykorzystuje pięć kluczy, z których wszystkie znajdują się w build/target/product/security :

klucz testowy
Ogólny klucz domyślny dla pakietów, które w inny sposób nie określają klucza.
platforma
Klucz testowy dla pakietów będących częścią platformy podstawowej.
wspólny
Klucz testowy dla rzeczy udostępnianych w procesie domu/kontaktów.
głoska bezdźwięczna
Klucz testowy dla pakietów będących częścią systemu multimediów/pobierania.
stos sieciowy
Klucz testowy dla pakietów będących częścią systemu sieciowego. Klucz networkstack służy do podpisywania plików binarnych zaprojektowanych jako komponenty systemu modułowego . Jeśli aktualizacje modułów są budowane osobno i zintegrowane jako gotowe wersje z obrazem urządzenia, generowanie klucza networkstack w drzewie źródeł Androida może nie być konieczne.

Poszczególne pakiety określają jeden z tych kluczy, ustawiając LOCAL_CERTIFICATE w swoim pliku Android.mk. (testkey jest używany, jeśli ta zmienna nie jest ustawiona.) Możesz także określić zupełnie inny klucz poprzez nazwę ścieżki, np.:

device/yoyodyne/apps/SpecialApp/Android.mk
 [...]

LOCAL_CERTIFICATE := device/yoyodyne/security/special

Teraz kompilacja używa klucza device/yoyodyne/security/special.{x509.pem,pk8} do podpisywania pliku SpecialApp.apk. W kompilacji można używać wyłącznie kluczy prywatnych, które nie są chronione hasłem.

Zaawansowane opcje podpisywania

Wymiana klucza podpisującego APK

Skrypt sign_target_files_apks działa na plikach docelowych wygenerowanych dla kompilacji. Wszystkie informacje o certyfikatach i kluczach prywatnych używanych w czasie kompilacji są zawarte w plikach docelowych. Podczas uruchamiania skryptu podpisywania w celu podpisania wydania klucze podpisu można zastąpić na podstawie nazwy klucza lub nazwy pliku APK.

Użyj flag --key_mapping i --default_key_mappings , aby określić zamianę klucza na podstawie nazw kluczy:

  • Flaga --key_mapping src_key = dest_key określa zamianę jednego klucza na raz.
  • Flaga --default_key_mappings dir określa katalog z pięcioma kluczami, które mają zastąpić wszystkie klucze w build/target/product/security ; jest to równoznaczne z pięciokrotnym użyciem --key_mapping w celu określenia mapowań.
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

Użyj --extra_apks apk_name1,apk_name2,... = key aby określić zamienniki kluczy podpisujących na podstawie nazw plików APK. Jeśli key pozostanie pusty, skrypt traktuje określone pliki APK jako wstępnie podpisane.

W przypadku hipotetycznego produktu tardis potrzebujesz sześciu kluczy chronionych hasłem: pięć do zastąpienia pięciu kluczy w build/target/product/security i jeden do zastąpienia dodatkowego klucza device/yoyodyne/security/special wymaganego przez SpecialApp w powyższym przykładzie. Jeżeli klucze znajdowały się w następujących plikach:

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

Następnie podpisałbyś wszystkie aplikacje w ten sposób:

./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

Wyświetla się następujące informacje:

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.

Po poproszeniu użytkownika o podanie haseł do wszystkich kluczy chronionych hasłem skrypt ponownie podpisuje wszystkie pliki APK w docelowym wejściowym pliku .zip za pomocą kluczy zwalniających. Przed uruchomieniem polecenia możesz także ustawić zmienną środowiskową ANDROID_PW_FILE na tymczasową nazwę pliku; następnie skrypt wywołuje edytor, aby umożliwić wprowadzenie haseł dla wszystkich kluczy (może to być wygodniejszy sposób wprowadzania haseł).

Wymiana klucza podpisującego APEX

W Androidzie 10 wprowadzono format pliku APEX umożliwiający instalację modułów systemowych niższego poziomu. Jak wyjaśniono w podpisywania APEX , każdy plik APEX jest podpisany dwoma kluczami: jeden dla obrazu mini systemu plików w APEX, a drugi dla całego APEX.

Podczas podpisywania w celu wydania dwa klucze podpisujące plik APEX są zastępowane kluczami zwalniającymi. Klucz ładunku systemu plików jest określony za pomocą flagi --extra_apex_payload , a cały klucz podpisywania pliku APEX jest określony za pomocą flagi --extra_apks .

W przypadku produktu tardis załóżmy, że masz następującą konfigurację kluczy dla plików com.android.conscrypt.apex , com.android.media.apex i 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"

Masz następujące pliki zawierające klucze zwalniające:

vendor/yoyodyne/security/runtime_apex_container.x509.pem
vendor/yoyodyne/security/runtime_apex_container.pk8
vendor/yoyodyne/security/runtime_apex_payload.pem

Poniższe polecenie zastępuje klucze podpisywania dla com.android.runtime.release.apex i com.android.tzdata.apex podczas podpisywania wersji. W szczególności com.android.runtime.release.apex jest podpisany określonymi kluczami wydania ( runtime_apex_container dla pliku APEX i runtime_apex_payload dla ładunku obrazu pliku). com.android.tzdata.apex jest traktowany jako wstępnie podpisany. Wszystkie pozostałe pliki APEX są obsługiwane przez domyślną konfigurację wymienioną w plikach docelowych.

./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

Uruchomienie powyższego polecenia daje następujące logi:

        [...]
    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)
        [...]

Inne opcje

Skrypt sign_target_files_apks zapisuje opis kompilacji i odcisk palca w plikach właściwości kompilacji, aby odzwierciedlić, że kompilacja jest kompilacją podpisaną. Flaga --tag_changes kontroluje, jakie zmiany zostaną wprowadzone w odcisku palca. Uruchom skrypt z opcją -h , aby wyświetlić dokumentację dotyczącą wszystkich flag.

Ręcznie wygeneruj klucze

Android używa 2048-bitowych kluczy RSA z wykładnikiem publicznym 3. Możesz wygenerować pary certyfikatów/kluczy prywatnych za pomocą narzędzia openssl z openssl.org :

# generate RSA key
openssl 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 key
openssl 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 key
openssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt

# securely delete the temp.pem file
shred --remove temp.pem

Podane powyżej polecenie openssl pkcs8 tworzy plik .pk8 bez hasła, odpowiedni do użycia w systemie kompilacji. Aby utworzyć plik .pk8 zabezpieczony hasłem (co powinieneś zrobić dla wszystkich aktualnych kluczy wydania), zamień argument -nocrypt na -passout stdin ; następnie openssl zaszyfruje klucz prywatny hasłem odczytanym ze standardowego wejścia. Nie jest drukowany żaden monit, więc jeśli stdin jest terminalem, program będzie wyglądał na zawieszony, gdy w rzeczywistości będzie tylko czekał na wprowadzenie hasła. Można użyć innych wartości argumentu-passout, aby odczytać hasło z innych lokalizacji; szczegółowe informacje można znaleźć w dokumentacji openssl .

Plik pośredni temp.pem zawiera klucz prywatny bez żadnego zabezpieczenia hasłem, dlatego podczas generowania kluczy wydania należy go ostrożnie pozbyć się. W szczególności narzędzie GNUshred może nie działać w sieciowych lub kronikowanych systemach plików. Możesz użyć katalogu roboczego znajdującego się na dysku RAM (takim jak partycja tmpfs) podczas generowania kluczy, aby mieć pewność, że produkty pośrednie nie zostaną przypadkowo ujawnione.

Twórz pliki obrazów

Kiedy już masz signed-target_files.zip , musisz utworzyć obraz, aby móc umieścić go na urządzeniu. Aby utworzyć podpisany obraz z plików docelowych, uruchom następujące polecenie w katalogu głównym drzewa Androida:

img_from_target_files signed-target_files.zip signed-img.zip
Wynikowy plik, signed-img.zip , zawiera wszystkie pliki .img . Aby załadować obraz na urządzenie, użyj funkcji fastboot w następujący sposób:
fastboot update signed-img.zip