podpisywanie kompilacji do publikacji,

Obrazy systemu Android OS używają podpisów kryptograficznych w 2 miejscach:

  1. Każdy plik .apk w obrazie musi być podpisany. Menedżer pakietów Androida używa podpisu .apk na 2 sposoby:
    • Gdy aplikacja jest zastępowana, musi być podpisana tym samym kluczem co stara aplikacja, aby uzyskać dostęp do jej danych. Dotyczy to zarówno aktualizowania aplikacji użytkownika przez zastąpienie pliku .apk, jak i zastępowania aplikacji systemowej nowszą wersją zainstalowaną w folderze .apk./data
    • Jeśli co najmniej 2 aplikacje mają 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 test-keys w katalogu build/target/product/security. Podczas tworzenia obrazu systemu operacyjnego Android za pomocą narzędzia make wszystkie pliki .apk zostaną podpisane za pomocą kluczy testowych. Klucze testowe są publicznie znane, więc każdy może podpisać własne pliki APK tymi samymi kluczami, co może umożliwić mu zastąpienie lub przejęcie aplikacji systemowych wbudowanych w obraz systemu operacyjnego. Dlatego ważne jest, aby podpisywać każdy publicznie udostępniony lub wdrożony obraz systemu operacyjnego Android specjalnym zestawem kluczy wersji, do których masz wyłączny dostęp.

Aby wygenerować własny, niepowtarzalny zestaw kluczy wersji, uruchom te polecenia w katalogu głównym 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ło informacje o Twojej organizacji. Możesz użyć dowolnego katalogu, ale wybierz lokalizację, która jest zabezpieczona i ma kopię zapasową. Niektórzy dostawcy decydują się zaszyfrować klucz prywatny silnym hasłem i przechowywać zaszyfrowany klucz w systemie kontroli wersji, a inni przechowują klucze wersji w zupełnie innym miejscu, np. na komputerze odłączonym od sieci.

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 pliki docelowe .zip i tworzy nowe pliki docelowe .zip, w których wszystkie pliki .apk zostały podpisane nowymi kluczami. Nowo podpisane obrazy znajdziesz w sekcji IMAGES/signed-target_files.zip.

Podpisywanie pakietów OTA

Podpisany plik ZIP z plikami docelowymi można przekonwertować na podpisany plik ZIP z aktualizacją OTA, wykonując te czynności:
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

Podpisy i wczytywanie z zewnątrz

Instalacja z pominięciem nie omija normalnego mechanizmu weryfikacji podpisu pakietu w trybie recovery – przed zainstalowaniem pakietu tryb recovery sprawdzi, czy jest on podpisany jednym z kluczy prywatnych pasujących do kluczy publicznych przechowywanych w partycji recovery, tak jak w przypadku pakietu dostarczonego drogą bezprzewodową.

Pakiety aktualizacji otrzymywane z głównego systemu są zwykle weryfikowane 2 razy: raz przez główny system za pomocą metody RecoverySystem.verifyPackage() w interfejsie API Androida, a następnie ponownie przez odzyskiwanie. Interfejs RecoverySystem API sprawdza podpis na podstawie kluczy publicznych przechowywanych w systemie głównym w pliku /system/etc/security/otacerts.zip (domyślnie). Proces odzyskiwania sprawdza podpis za pomocą kluczy publicznych przechowywanych na dysku RAM partycji odzyskiwania w pliku /res/keys.

Domyślnie pliki docelowe .zip wygenerowane przez zestawy kompilacji ustawiają certyfikat OTA tak, aby pasował do klucza testowego. W przypadku opublikowanego obrazu 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 wersji z katalogu certs.

Obraz systemu i obraz odzyskiwania zwykle zawierają ten sam zestaw kluczy publicznych OTA. Dodanie klucza tylko do zestawu kluczy przywracania umożliwia podpisywanie pakietów, które można zainstalować tylko przez wczytywanie z boku (zakładając, że główny mechanizm pobierania aktualizacji systemu prawidłowo weryfikuje plik otacerts.zip). Możesz określić dodatkowe klucze, które mają być uwzględniane tylko w przypadku 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 klucz publicznyvendor/yoyodyne/security/tardis/sideload.x509.pem w pliku kluczy odzyskiwania, aby można było instalować pakiety podpisanevendor/yoyodyne/security/tardis/sideload.x509.pemtym kluczem. Dodatkowy klucz nie jest jednak uwzględniony w pliku otacerts.zip, więc systemy, które prawidłowo weryfikują pobrane pakiety, nie wywołują odzyskiwania w przypadku pakietów podpisanych tym kluczem.

Certyfikaty i klucze prywatne

Każdy klucz jest dostępny w 2 plikach: certyfikacie z rozszerzeniem .x509.pem i kluczu prywatnym z rozszerzeniem .pk8. Klucz prywatny należy przechowywać w tajemnicy. Jest on potrzebny do podpisywania pakietu. Klucz może być chroniony hasłem. Certyfikat zawiera tylko publiczną część klucza, więc można go rozpowszechniać na szeroką skalę. Służy do weryfikowania, czy pakiet został podpisany odpowiednim kluczem prywatnym.

Standardowa kompilacja Androida używa 5 kluczy, które znajdują się w  build/target/product/security:

testkey
Ogólny domyślny klucz dla pakietów, które nie określają klucza w inny sposób.
platform
Klucz testowy dla pakietów, które są częścią platformy podstawowej.
udostępnioną
Klucz testowy dla elementów udostępnianych w procesie dotyczącym domu lub kontaktów.
multimedia
Klucz testowy dla pakietów, które są częścią systemu multimediów lub pobierania.
stos sieciowy
Klucz testowy dla pakietów, które są częścią systemu sieciowego. Klucz networkstack służy do podpisywania plików binarnych zaprojektowanych jako modułowe komponenty systemu . Jeśli aktualizacje modułów są tworzone oddzielnie i integrowane jako wstępnie skompilowane moduły w obrazie urządzenia, nie musisz generować klucza networkstack w drzewie źródłowym Androida.

Poszczególne pakiety określają jeden z tych kluczy, ustawiając LOCAL_CERTIFICATE w pliku Android.mk. (Jeśli ta zmienna nie jest ustawiona, używany jest klucz testowy). Możesz też określić zupełnie inny klucz, podając ścieżkę do niego, 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 podpisania pliku SpecialApp.apk. Kompilacja może używać tylko kluczy prywatnych, które nie są chronione hasłem.

Zaawansowane opcje podpisywania

Zastępowanie klucza podpisywania APK

Skrypt podpisywania sign_target_files_apks działa na plikach docelowych wygenerowanych na potrzeby 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 wersji klucze podpisywania można zastąpić na podstawie nazwy klucza lub nazwy pliku APK.

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

  • Flaga --key_mapping src_key=dest_key określa zamiennik dla jednego klawisza naraz.
  • Flaga --default_key_mappings dir określa katalog z 5 kluczami, które zastępują wszystkie klucze w build/target/product/security. Jest to odpowiednik użycia --key_mapping 5 razy 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 flagi --extra_apks apk_name1,apk_name2,...=key, aby określić zamiany kluczy podpisywania na podstawie nazw plików APK. Jeśli pole key jest puste, skrypt traktuje określone pliki APK jako wstępnie podpisane.

W przypadku hipotetycznego produktu tardis potrzebujesz 6 kluczy chronionych hasłem: 5 do zastąpienia 5 kluczy w build/target/product/security i 1 do zastąpienia dodatkowego klucza device/yoyodyne/security/special wymaganego przez aplikację SpecialApp w powyższym przykładzie. Jeśli klucze znajdowały się w tych 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 podpisz 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

Spowoduje to wyświetlenie tych 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 użytkownikowi prośby o podanie haseł do wszystkich kluczy chronionych hasłem skrypt ponownie podpisuje wszystkie pliki APK w docelowym folderze wejściowym .zip za pomocą kluczy wersji. Przed uruchomieniem polecenia możesz też ustawić zmienną środowiskową ANDROID_PW_FILE na nazwę pliku tymczasowego. Skrypt wywoła wtedy edytor, aby umożliwić Ci wpisanie haseł do wszystkich kluczy (może to być wygodniejszy sposób wpisywania haseł).

Wymiana klucza podpisywania pakietu APEX

W Androidzie 10 wprowadzono format pliku APEX do instalowania modułów systemowych niższego poziomu. Jak wyjaśniono w sekcji Podpisywanie pakietów APEX, każdy plik APEX jest podpisywany 2 kluczami: jednym dla obrazu miniaturowego systemu plików w pakiecie APEX, a drugim dla całego pakietu APEX.

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

W przypadku produktu tardis załóżmy, że masz tę konfigurację kluczy dla plików APEX com.android.conscrypt.apex, com.android.media.apexcom.android.runtime.release.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 te 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

Podczas podpisywania wersji to polecenie zastępuje klucze podpisywania dla środowisk com.android.runtime.release.apexcom.android.tzdata.apex. W szczególności plik com.android.runtime.release.apex jest podpisany określonymi kluczami wersji (runtime_apex_container w przypadku pliku APEX i runtime_apex_payload w przypadku ładunku obrazu pliku). com.android.tzdata.apex jest traktowany jako podpisany wcześniej. Wszystkie inne 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 spowoduje wyświetlenie tych logów:

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

sign_target_files_apksSkrypt podpisywania zmienia opis kompilacji i odcisk palca w plikach właściwości kompilacji, aby odzwierciedlić fakt, że kompilacja jest podpisana. Flaga --tag_changes określa, jakie zmiany zostaną wprowadzone w odcisku palca. Uruchom skrypt z parametrem -h, aby wyświetlić dokumentację wszystkich flag.

Ręczne generowanie kluczy

Android używa 2048-bitowych kluczy RSA z wykładnikiem publicznym 3. Pary certyfikatów i kluczy prywatnych możesz wygenerować 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, który można używać w systemie kompilacji. Aby utworzyć zabezpieczony hasłem plik .pk8 (co należy zrobić w przypadku wszystkich rzeczywistych kluczy wersji), zastąp argument -nocrypt argumentem -passout stdin. Następnie OpenSSL zaszyfruje klucz prywatny hasłem odczytanym ze standardowego wejścia. Nie jest wyświetlany żaden prompt, więc jeśli stdin jest terminalem, program będzie wyglądał na zawieszony, gdy w rzeczywistości będzie tylko czekał na wpisanie hasła. W przypadku argumentu -passout można użyć innych wartości, aby odczytać hasło z innych lokalizacji. Więcej informacji znajdziesz w  dokumentacji OpenSSL.

Plik pośredni temp.pem zawiera klucz prywatny bez żadnego hasła, więc podczas generowania kluczy wersji należy go odpowiednio usunąć. W szczególności narzędzie GNUshred może nie działać skutecznie w przypadku sieciowych systemów plików lub systemów plików z dziennikiem. Podczas generowania kluczy możesz używać katalogu roboczego znajdującego się na dysku RAM (np. partycji tmpfs), aby mieć pewność, że pliki pośrednie nie zostaną przypadkowo ujawnione.

Tworzenie plików obrazów

Gdy masz signed-target_files.zip, musisz utworzyć obraz, aby można go było umieścić na urządzeniu. Aby utworzyć podpisany obraz z plików docelowych, uruchom to 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 wgrać obraz na urządzenie, użyj fastboota w ten sposób:
fastboot update signed-img.zip