Podpisywanie kompilacji do wydania

Obrazy systemu operacyjnego Android używają podpisów kryptograficznych w dwóch miejscach:

  1. Każdy plik .apk w obrazie musi być podpisany. Menedżer pakietów Androida używa podpisu .apk na dwa sposoby:
    • Gdy aplikacja jest zastępowana, musi być podpisana tym samym kluczem, co stara aplikacja, aby uzyskać dostęp do danych starej aplikacji. Dotyczy to zarówno aktualizowania aplikacji użytkownika przez nadpisanie .apk , jak i zastępowania aplikacji systemowej nowszą wersją zainstalowaną w /data .
    • Jeśli co najmniej dwie aplikacje chcą udostępniać 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 za pomocą kluczy testowych. Ponieważ klucze testowe są publicznie znane, każdy może podpisać własne pliki .apk tymi samymi kluczami, co może umożliwić im zastąpienie lub przejęcie aplikacji systemowych wbudowanych w obraz systemu operacyjnego. Z tego powodu bardzo ważne jest podpisanie każdego publicznie wydanego lub wdrożonego obrazu systemu operacyjnego Android za pomocą specjalnego zestawu kluczy wydania, do którego tylko Ty masz dostęp.

Aby wygenerować swój własny, unikalny zestaw kluczy wydania, uruchom te 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 odzwierciedlić informacje o Twojej organizacji. Możesz użyć dowolnego katalogu, ale uważaj, aby wybrać lokalizację, w której utworzono kopię zapasową i która 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 innym miejscu, na przykład na komputerze z luką powietrzną.

Aby wygenerować obraz wersji, 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

Skrypt sign_target_files_apks przyjmuje jako dane wejściowe plik target-files .zip i tworzy nowy plik target-files .zip , w którym wszystkie pliki .apk zostały podpisane nowymi kluczami. Nowo podpisane obrazy można znaleźć w folderze IMAGES/ w signed-target_files.zip .

Podpisywanie pakietów OTA

Podpisany plik zip z plikami docelowymi można przekonwertować na podpisany plik zip aktualizacji OTA przy użyciu następującej procedury:
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

Podpisy i sideloading

Ładowanie boczne nie omija normalnego mechanizmu weryfikacji podpisu pakietu — przed zainstalowaniem pakietu proces odzyskiwania zweryfikuje, czy jest on podpisany jednym z kluczy prywatnych zgodnych z kluczami publicznymi przechowywanymi na partycji odzyskiwania, tak jak w przypadku pakietu dostarczanego 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 API Androida, a następnie ponownie przez odzyskiwanie. RecoverySystem API porównuje podpis z kluczami publicznymi przechowywanymi w systemie głównym, w pliku /system/etc/security/otacerts.zip (domyślnie). Recovery porównuje podpis z kluczami publicznymi przechowywanymi na dysku RAM partycji odzyskiwania w pliku /res/keys .

Domyślnie plik .zip plików docelowych utworzony przez kompilację ustawia certyfikat OTA tak, aby był zgodny z kluczem testowym. Na wydanym 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 certs.

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 sideloading (zakładając, że główny mechanizm pobierania aktualizacji systemu poprawnie przeprowadza weryfikację z otacerts.zip). Możesz określić dodatkowe klucze, które będą 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, dzięki czemu może on instalować podpisane za jego pomocą pakiety. Dodatkowy klucz nie jest jednak zawarty w otacerts.zip, więc systemy, które poprawnie weryfikują pobrane pakiety, nie wywołują odzyskiwania pakietów podpisanych tym kluczem.

Certyfikaty i klucze prywatne

Każdy klucz znajduje się w dwóch plikach: certyfikat , który ma rozszerzenie .x509.pem i klucz prywatny , który ma rozszerzenie .pk8. Klucz prywatny powinien być utrzymywany w tajemnicy i jest potrzebny do podpisania pakietu. Sam klucz może być chroniony hasłem. Natomiast certyfikat zawiera tylko publiczną połowę klucza, dzięki czemu może być szeroko rozpowszechniany. Służy do weryfikacji, czy pakiet został podpisany odpowiednim kluczem prywatnym.

Standardowa kompilacja Androida używa pięciu 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, które są częścią platformy podstawowej.
wspólny
Testuj klucz dla rzeczy, które są udostępniane w procesie domu/kontaktów.
głoska bezdźwięczna
Klucz testowy dla pakietów, które są częścią systemu mediów/pobierania.
stos sieci
Klucz testowy dla pakietów, które są częścią systemu sieciowego. Klucz Networkstack służy do podpisywania plików binarnych zaprojektowanych jako składniki systemu modułowego . Jeśli aktualizacje modułów są kompilowane oddzielnie i zintegrowane jako gotowe wersje w obrazie urządzenia, generowanie klucza stosu sieciowego w drzewie źródłowym systemu Android 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 również określić zupełnie inny klucz przez nazwę ścieżki, np.:

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

LOCAL_CERTIFICATE := device/yoyodyne/security/special

Teraz kompilacja używa device/yoyodyne/security/special.{x509.pem,pk8} do podpisywania SpecialApp.apk. Kompilacja może używać tylko kluczy prywatnych, które nie są chronione hasłem.

Zaawansowane opcje podpisywania

Wymiana klucza podpisywania APK

Skrypt podpisywania 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 do wydania klucze podpisywania można zastąpić na podstawie nazwy klucza lub nazwy APK.

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

  • Flaga --key_mapping src_key = dest_key określa zastępowanie jednego klucza na raz.
  • Flaga katalogu --default_key_mappings dir określa katalog z pięcioma kluczami zastępującymi wszystkie klucze w build/target/product/security ; jest to równoważne pięciokrotnemu użyciu --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 klucza podpisywania na podstawie nazw 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ęciu do zastąpienia pięciu w build/target/product/security i jednego do zastąpienia dodatkowego klucza device/yoyodyne/security/special wymaganego przez SpecialApp w powyższym przykładzie. Jeśli 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 podpisujesz 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

Powoduje to wyświetlenie następujących informacji:

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 wyświetleniu monitu o podanie haseł dla wszystkich kluczy chronionych hasłem skrypt ponownie podpisuje wszystkie pliki APK w docelowym pliku .zip wejściowym za pomocą kluczy zwalniających. Przed uruchomieniem polecenia możesz również ustawić zmienną środowiskową ANDROID_PW_FILE na tymczasową nazwę pliku; skrypt następnie wywołuje edytor, aby umożliwić wprowadzenie haseł dla wszystkich kluczy (może to być wygodniejszy sposób wprowadzania haseł).

Wymiana klucza podpisywania APEX

Android 10 wprowadza format pliku APEX do instalowania modułów systemowych niższego poziomu. Jak wyjaśniono w podpisaniu APEX , każdy plik APEX jest podpisany dwoma kluczami: jednym dla obrazu mini systemu plików w APEX, a drugim dla całego APEX.

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

W przypadku produktu tardis załóżmy, że masz następującą konfigurację klucza 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 wersji:

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

Następujące polecenie zastępuje klucze podpisywania dla com.android.runtime.release.apex i com.android.tzdata.apex podczas podpisywania wydania. W szczególności com.android.runtime.release.apex jest podpisany przy użyciu określonych kluczy 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 inne pliki APEX są obsługiwane przez konfigurację domyślną, jak podano 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 podpisywania sign_target_files_apks przepisuje 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 są wprowadzane w odcisku palca. Uruchom skrypt z -h , aby zobaczyć dokumentację dotyczącą wszystkich flag.

Ręczne generowanie kluczy

Android używa 2048-bitowych kluczy RSA z publicznym wykładnikiem 3. Możesz wygenerować pary certyfikat/klucz prywatny za pomocą narzędzia openssl ze strony 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

Podana powyżej komenda openssl pkcs8 tworzy plik .pk8 bez hasła, odpowiedni do użycia z systemem budowania. Aby utworzyć plik .pk8 zabezpieczony hasłem (co powinieneś zrobić dla wszystkich aktualnych kluczy wydania), zastąp argument -nocrypt na -passout stdin ; wtedy openssl zaszyfruje klucz prywatny hasłem odczytanym ze standardowego wejścia. Żaden monit nie jest wypisywany, więc jeśli terminalem jest stdin, program będzie się zawieszał, gdy tak naprawdę czeka tylko na wprowadzenie hasła. Inne wartości mogą być użyte dla argumentu-passout w celu odczytania hasła z innych lokalizacji; szczegółowe informacje można znaleźć w dokumentacji openssl .

Plik pośredni temp.pem zawiera klucz prywatny bez jakiejkolwiek ochrony hasłem, więc należy go starannie usunąć podczas generowania kluczy wydania. W szczególności narzędzie GNUshred może nie działać w sieci lub w kronikowanych systemach plików. Podczas generowania kluczy można użyć katalogu roboczego znajdującego się na dysku RAM (takiego jak partycja tmpfs), aby upewnić się, że produkty pośrednie nie zostaną przypadkowo ujawnione.

Tworzenie plików graficznych

Po stworzeniu pliku signature-target-files.zip należy utworzyć obraz, aby można go było umieścić na urządzeniu. Aby utworzyć podpisany obraz z plików docelowych, uruchom następujące polecenie z katalogu głównego 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 fastboot w następujący sposób:
fastboot update signed-img.zip