Schemat podpisu pliku APK w wersji 2

Schemat podpisu APK v2 to schemat podpisu całego pliku, który zwiększa szybkość weryfikacji i wzmacnia gwarancje integralności poprzez wykrywanie wszelkich zmian w chronionych częściach pliku APK.

Podpisywanie przy użyciu schematu podpisu APK v2 wstawia blok podpisywania APK do pliku APK bezpośrednio przed sekcją centralnego katalogu ZIP. Wewnątrz bloku podpisywania APK podpisy w wersji 2 i informacje o tożsamości osoby podpisującej są przechowywane w bloku schematu podpisu APK w wersji 2 .

APK przed i po podpisaniu

Rysunek 1. APK przed i po podpisaniu

Schemat podpisu APK v2 został wprowadzony w systemie Android 7.0 (Nougat). Aby plik APK można było zainstalować na urządzeniach z Androidem 6.0 (Marshmallow) i starszych, plik APK powinien zostać podpisany przy użyciu podpisu JAR przed podpisaniem w schemacie v2.

Blok podpisywania APK

Aby zachować kompatybilność wsteczną z formatem APK w wersji 1, podpisy plików APK w wersji 2 i nowszych są przechowywane w bloku podpisu APK, nowym kontenerze wprowadzonym do obsługi schematu podpisów APK w wersji 2. W pliku APK blok podpisywania APK znajduje się bezpośrednio przed centralnym katalogiem ZIP, który znajduje się na końcu pliku.

Blok zawiera pary identyfikator-wartość zawinięte w sposób ułatwiający zlokalizowanie bloku w pliku APK. Podpis v2 pakietu APK jest przechowywany jako para identyfikator-wartość o identyfikatorze 0x7109871a.

Format

Format bloku podpisu APK jest następujący (wszystkie pola numeryczne mają kształt Little-Endian):

  • size of block w bajtach (bez tego pola) (uint64)
  • Sekwencja par identyfikator-wartość z przedrostkiem długości uint64:
    • ID (uint32)
    • value (zmienna długość: długość pary - 4 bajty)
  • size of block w bajtach — taki sam jak pierwsze pole (uint64)
  • magic „APK Sig Block 42” (16 bajtów)

Plik APK jest analizowany poprzez znalezienie początku katalogu centralnego ZIP (poprzez znalezienie rekordu końca katalogu centralnego ZIP na końcu pliku, a następnie odczytanie przesunięcia początkowego katalogu centralnego z rekordu). Wartość magic pozwala szybko ustalić, czy to, co poprzedza Central Directory, jest prawdopodobnie blokiem podpisywania pliku APK. size of block skutecznie wskazuje początek bloku w pliku.

Pary identyfikator-wartość o nieznanych identyfikatorach należy ignorować podczas interpretacji bloku.

Blok schematu podpisu APK v2

Plik APK jest podpisywany przez co najmniej jednego sygnatariusza/tożsamość, z których każda jest reprezentowana przez klucz podpisu. Informacje te są przechowywane jako blok schematu podpisu APK v2. Dla każdego podpisującego przechowywane są następujące informacje:

  • (algorytm podpisu, skrót, podpis) krotki. Skrót jest przechowywany w celu oddzielenia weryfikacji podpisu od sprawdzania integralności zawartości pliku APK.
  • Łańcuch certyfikatów X.509 reprezentujący tożsamość osoby podpisującej.
  • Dodatkowe atrybuty jako pary klucz-wartość.

Dla każdego podpisującego plik APK jest weryfikowany przy użyciu obsługiwanego podpisu z dostarczonej listy. Podpisy z nieznanymi algorytmami podpisu są ignorowane. Do każdej implementacji należy wybór, który podpis ma zostać użyty w przypadku napotkania wielu obsługiwanych podpisów. Umożliwia to wprowadzenie w przyszłości silniejszych metod podpisywania w sposób kompatybilny wstecz. Sugerowane podejście polega na weryfikacji najsilniejszego podpisu.

Format

Blok podpisu APK w wersji 2 jest przechowywany w bloku podpisywania APK pod identyfikatorem 0x7109871a .

Format bloku schematu podpisu APK v2 jest następujący (wszystkie wartości liczbowe to Little-Endian, wszystkie pola z prefiksem długości używają długości uint32):

  • sekwencja signer z przedrostkiem długości:
    • signed data z prefiksem długości:
      • sekwencja skrótów z digests długości:
      • sekwencja certificates X.509 z prefiksem długości:
        • certificate X.509 z prefiksem długości (formularz ASN.1 DER)
      • sekwencja additional attributes z przedrostkiem długości:
        • ID (uint32)
        • value (zmienna długość: długość dodatkowego atrybutu - 4 bajty)
    • sekwencja signatures z przedrostkiem długości:
      • signature algorithm ID (uint32)
      • signature z przedrostkiem długości nad signed data
    • public key z prefiksem długości (SubjectPublicKeyInfo, formularz ASN.1 DER)

Identyfikatory algorytmów podpisów

  • 0x0101 — RSASSA-PSS z podsumowaniem SHA2-256, SHA2-256 MGF1, 32 bajty soli, zwiastun: 0xbc
  • 0x0102 — RSASSA-PSS z podsumowaniem SHA2-512, SHA2-512 MGF1, 64 bajty soli, zwiastun: 0xbc
  • 0x0103 — RSASSA-PKCS1-v1_5 ze skrótem SHA2-256. Dotyczy to systemów kompilacji, które wymagają podpisów deterministycznych.
  • 0x0104 — RSASSA-PKCS1-v1_5 z skrótem SHA2-512. Dotyczy to systemów kompilacji, które wymagają podpisów deterministycznych.
  • 0x0201 — ECDSA z skrótem SHA2-256
  • 0x0202 — ECDSA z skrótem SHA2-512
  • 0x0301 — DSA z skrótem SHA2-256

Wszystkie powyższe algorytmy podpisu są obsługiwane przez platformę Android. Narzędzia do podpisywania mogą obsługiwać podzbiór algorytmów.

Obsługiwane rozmiary klawiszy i krzywe EC:

  • RPA: 1024, 2048, 4096, 8192, 16384
  • WE: NIST P-256, P-384, P-521
  • DSA: 1024, 2048, 3072

Treść chroniona integralnością

W celu ochrony zawartości pliku APK plik APK składa się z czterech sekcji:

  1. Zawartość wpisów ZIP (od przesunięcia 0 do początku bloku podpisywania APK)
  2. Blok podpisywania APK
  3. Centralny katalog ZIP
  4. ZIP Koniec katalogu centralnego

Sekcje APK po podpisaniu

Rysunek 2. Sekcje APK po podpisaniu

Schemat podpisu APK v2 chroni integralność sekcji 1, 3, 4 i signed data bloku schematu podpisu APK v2 zawartego w sekcji 2.

Integralność sekcji 1, 3 i 4 jest chroniona przez jeden lub więcej skrótów ich zawartości przechowywanych w signed data które z kolei są chronione przez jeden lub więcej podpisów.

Podsumowanie w sekcjach 1, 3 i 4 jest obliczane w następujący sposób, podobnie jak w dwupoziomowym drzewie Merkle'a . Każda sekcja jest podzielona na kolejne fragmenty o wielkości 1 MB (2 20 bajtów). Ostatni fragment w każdej sekcji może być krótszy. Skrót każdego fragmentu jest obliczany na podstawie konkatenacji bajtu 0xa5 , długości fragmentu w bajtach (little-endian uint32) i zawartości fragmentu. Skrót najwyższego poziomu jest obliczany na podstawie połączenia bajtu 0x5a , liczby fragmentów (little-endian uint32) i połączenia skrótów fragmentów w kolejności, w jakiej pojawiają się one w pliku APK. Podsumowanie jest obliczane fragmentarycznie, aby umożliwić przyspieszenie obliczeń poprzez ich równoległość.

Podsumowanie APK

Rysunek 3. Podsumowanie pliku APK

Ochrona sekcji 4 (Koniec ZIP Centralnego Katalogu) jest skomplikowana przez sekcję zawierającą przesunięcie Centralnego Katalogu ZIP. Przesunięcie zmienia się, gdy zmienia się rozmiar bloku podpisywania pliku APK, na przykład po dodaniu nowego podpisu. Zatem podczas obliczania podsumowania na końcu ZIP w Central Directory, pole zawierające przesunięcie Centralnego Katalogu ZIP należy traktować jako zawierające przesunięcie bloku podpisywania APK.

Zabezpieczenia przed wycofaniem

Osoba atakująca może podjąć próbę zweryfikowania pliku APK podpisanego w wersji 2 jako pliku APK podpisanego w wersji 1 na platformach Android, które obsługują weryfikację pliku APK podpisanego w wersji 2. Aby złagodzić ten atak, pliki APK podpisane w wersji 2, które są również podpisane w wersji 1, muszą zawierać atrybut X-Android-APK-Signed w głównej sekcji plików META-INF/*.SF. Wartością atrybutu jest oddzielony przecinkami zestaw identyfikatorów schematów podpisów APK (identyfikator tego schematu to 2). Podczas weryfikacji podpisu v1 weryfikator APK ma obowiązek odrzucić pliki APK, które nie mają podpisu dla preferowanego przez weryfikatora schematu podpisów APK z tego zestawu (np. schemat v2). Ochrona ta opiera się na fakcie, że zawartość plików META-INF/*.SF jest chroniona sygnaturami v1. Zobacz sekcję dotyczącą weryfikacji pliku APK podpisanego w formacie JAR .

Osoba atakująca może podjąć próbę usunięcia silniejszych podpisów z bloku schematu podpisów APK v2. Aby złagodzić ten atak, lista identyfikatorów algorytmów podpisu, za pomocą których podpisano plik APK, jest przechowywana w signed data , który jest chroniony każdym podpisem.

Weryfikacja

W systemie Android 7.0 i nowszych wersjach pliki APK można weryfikować zgodnie ze schematem podpisu APK v2+ lub podpisywaniem JAR (schemat v1). Starsze platformy ignorują podpisy v2 i weryfikują jedynie podpisy v1.

Proces weryfikacji podpisu APK

Rysunek 4. Proces weryfikacji podpisu APK (nowe kroki na czerwono)

Weryfikacja schematu podpisu APK v2

  1. Znajdź blok podpisywania pliku APK i sprawdź, czy:
    1. Dwa pola rozmiaru bloku podpisywania APK zawierają tę samą wartość.
    2. Bezpośrednio po ZIP Central Directory następuje rekord ZIP End of Central Directory.
    3. Po zakończeniu ZIP Central Directory nie następuje więcej danych.
  2. Znajdź pierwszy blok schematu podpisu APK v2 wewnątrz bloku podpisywania APK. Jeśli blok v2 jest obecny, przejdź do kroku 3. W przeciwnym razie wróć do weryfikacji pliku APK przy użyciu schematu v1 .
  3. Dla każdego signer w bloku schematu podpisu APK v2:
    1. Wybierz najsilniejszy obsługiwany signature algorithm ID z signatures . Kolejność mocy zależy od każdej wersji implementacji/platformy.
    2. Zweryfikuj odpowiedni signature z signatures względem signed data przy użyciu public key . (Teraz można bezpiecznie analizować signed data .)
    3. Sprawdź, czy uporządkowana lista identyfikatorów algorytmów podpisu w digests i signatures jest identyczna. (Zapobiega to usuwaniu/dodawaniu podpisów.)
    4. Oblicz skrót zawartości APK , używając tego samego algorytmu skrótu, co algorytm skrótu używany przez algorytm podpisu.
    5. Sprawdź digests digest
    6. Sprawdź, czy SubjectPublicKeyInfo pierwszego certificate certificates jest identyczny z public key .
  4. Weryfikacja przebiegła pomyślnie, jeśli odnaleziono co najmniej jednego signer i dla każdego znalezionego signer powiódł się krok 3.

Uwaga : pliku APK nie można weryfikować przy użyciu schematu v1, jeśli w kroku 3 lub 4 wystąpi błąd.

Weryfikacja pliku APK podpisanego przez JAR (schemat v1)

Plik APK z podpisem JAR to standardowo podpisany plik JAR , który musi zawierać dokładnie wpisy wymienione w META-INF/MANIFEST.MF i gdzie wszystkie wpisy muszą być podpisane przez ten sam zestaw sygnatariuszy. Jego integralność sprawdza się w następujący sposób:

  1. Każdy sygnatariusz jest reprezentowany przez wpis JAR META-INF/<signer>.SF i META-INF/<signer>.(RSA|DSA|EC).
  2. <signer>.(RSA|DSA|EC) to PKCS #7 CMS ContentInfo ze strukturą SignedData , którego podpis jest weryfikowany za pomocą pliku <signer>.SF.
  3. Plik <signer>.SF zawiera podsumowanie całego pliku META-INF/MANIFEST.MF oraz skróty każdej sekcji META-INF/MANIFEST.MF. Sprawdzane jest podsumowanie całego pliku MANIFEST.MF. Jeśli to się nie powiedzie, zamiast tego sprawdzane jest podsumowanie każdej sekcji MANIFEST.MF.
  4. META-INF/MANIFEST.MF zawiera dla każdego wpisu JAR chronionego integralnością sekcję o odpowiedniej nazwie, zawierającą skrót nieskompresowanej zawartości wpisu. Wszystkie te podsumowania są weryfikowane.
  5. Weryfikacja pliku APK nie powiedzie się, jeśli plik APK zawiera wpisy JAR, które nie są wymienione w pliku MANIFEST.MF i nie są częścią podpisu JAR.

Łańcuch ochrony to zatem <podpisujący>.(RSA|DSA|EC) -> <podpisujący>.SF -> MANIFEST.MF -> zawartość każdego wpisu JAR chronionego integralnością.