Schemat podpisu APK v2

APK Signature Scheme 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 APK.

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

APK przed i po podpisaniu

Rysunek 1. APK przed i po podpisaniu

APK Signature Scheme v2 został wprowadzony w systemie Android 7.0 (Nougat). Aby można było zainstalować pakiet APK na urządzeniach z Androidem 6.0 (Marshmallow) i starszych, należy go podpisać przy użyciu podpisywania JAR przed podpisaniem przy użyciu schematu v2.

Blok podpisywania APK

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

Blok zawiera pary identyfikator-wartość opakowane w sposób, który ułatwia zlokalizowanie bloku w APK. Sygnatura v2 APK jest przechowywana jako para identyfikator-wartość o identyfikatorze 0x7109871a.

Format

Format bloku podpisywania APK jest następujący (wszystkie pola numeryczne to little-endian):

  • size of block w bajtach (z wyłączeniem 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 w pierwszym polu (uint64)
  • magic „APK Sig Block 42” (16 bajtów)

Plik APK jest analizowany przez odnalezienie początku katalogu centralnego ZIP (poprzez znalezienie rekordu Koniec katalogu centralnego ZIP na końcu pliku, a następnie odczytanie przesunięcia początku katalogu centralnego z rekordu). magic wartość zapewnia szybki sposób ustalenia, że ​​to, co poprzedza katalog centralny, jest prawdopodobnie blokiem podpisywania APK. size of block wtedy efektywnie wskazuje początek bloku w pliku.

Pary ID-wartość z nieznanymi ID powinny być ignorowane podczas interpretacji bloku.

Blok schematu podpisu APK v2

Plik APK jest podpisany przez co najmniej jednego sygnatariusza/tożsamość, z których każdy jest reprezentowany przez klucz podpisywania. Te informacje są przechowywane jako blok schematu podpisu APK v2. Dla każdego sygnatariusza 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 APK.
  • Łańcuch certyfikatów X.509 reprezentujący tożsamość osoby podpisującej.
  • Dodatkowe atrybuty jako pary klucz-wartość.

W przypadku każdego sygnatariusza plik APK jest weryfikowany przy użyciu obsługiwanego podpisu z dostarczonej listy. Podpisy z nieznanymi algorytmami podpisu są ignorowane. Każda implementacja decyduje, którego podpisu użyć, gdy napotka wiele obsługiwanych podpisów. Umożliwia to wprowadzenie w przyszłości silniejszych metod podpisywania w sposób zgodny z poprzednimi wersjami. Sugerowanym podejściem jest weryfikacja najsilniejszego podpisu.

Format

Blok APK Signature Scheme v2 jest przechowywany w bloku podpisywania APK pod identyfikatorem 0x7109871a .

Format bloku APK Signature Scheme v2 jest następujący (wszystkie wartości liczbowe to little-endian, wszystkie pola z przedrostkiem długości używają uint32 jako długości):

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

Identyfikatory algorytmu podpisu

  • 0x0101 — RSASSA-PSS z skrótem 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. To jest dla systemów budowania, które wymagają deterministycznych podpisów.
  • 0x0104 — RSASSA-PKCS1-v1_5 ze skrótem SHA2-512. To jest dla systemów budowania, które wymagają deterministycznych podpisów.
  • 0x0201 — ECDSA ze skrótem SHA2-256
  • 0x0202 — ECDSA ze 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ści chronione integralnością

W celu ochrony zawartości APK, 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. Koniec ZIP centralnego katalogu

Sekcje APK po podpisaniu

Rysunek 2. Sekcje APK po podpisaniu

APK Signature Scheme v2 chroni integralność sekcji 1, 3, 4 i signed data APK Signature Scheme v2 Block zawartych 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 . Każda sekcja jest podzielona na kolejne porcje o wielkości 1 MB (2 20 bajtów). Ostatnia porcja w każdej sekcji może być krótsza. Skrót każdej porcji jest obliczany na podstawie połączenia bajtu 0xa5 , długości porcji w bajtach (little-endian uint32) i zawartości porcji. 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 fragmenty pojawiają się w pliku APK. Skrót jest obliczany w sposób fragmentaryczny, aby umożliwić przyspieszenie obliczeń poprzez ich zrównoleglenie.

Podsumowanie APK

Rysunek 3. Podsumowanie 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 APK, na przykład po dodaniu nowego podpisu. Dlatego podczas obliczania skrótu na końcu ZIP centralnego katalogu pole zawierające przesunięcie centralnego katalogu ZIP musi być traktowane jako zawierające przesunięcie bloku podpisywania APK.

Ochrona przed wycofaniem

Osoba atakująca może spróbować zweryfikować pakiet APK z podpisem v2 jako pakiet APK z podpisem v1 na platformach Android, które obsługują weryfikację pakietu APK z podpisem v2. Aby złagodzić ten atak, pakiety APK z podpisem w wersji 2, które są również podpisane w wersji 1, muszą zawierać atrybut X-Android-APK-Signed w głównej sekcji swoich plików META-INF/*.SF. Wartość atrybutu to oddzielony przecinkami zestaw identyfikatorów schematu podpisu APK (identyfikator tego schematu to 2). Podczas weryfikacji podpisu v1 weryfikator APK musi odrzucić pakiety APK, które nie mają podpisu dla schematu podpisu APK preferowanego przez weryfikatora z tego zestawu (np. schemat v2). Ochrona ta polega na tym, że zawartość plików META-INF/*.SF jest chroniona sygnaturami v1. Zobacz sekcję dotyczącą weryfikacji podpisanego pakietu JAR APK .

Atakujący może próbować usunąć silniejsze podpisy z bloku APK Signature Scheme v2. Aby złagodzić ten atak, lista identyfikatorów algorytmów podpisu, którymi podpisano pakiet APK, jest przechowywana w signed data , który jest chroniony przez każdy podpis.

Weryfikacja

W systemie Android 7.0 i nowszych pakiety APK można zweryfikować zgodnie ze schematem podpisu APK v2+ lub podpisem JAR (schemat v1). Starsze platformy ignorują podpisy v2 i weryfikują tylko 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 APK i sprawdź, czy:
    1. Dwa pola rozmiaru bloku podpisywania APK zawierają tę samą wartość.
    2. Bezpośrednio po katalogu centralnym ZIP następuje wpis Koniec katalogu centralnego ZIP.
    3. Po zakończeniu ZIP centralnego katalogu nie następuje więcej danych.
  2. Znajdź pierwszy blok schematu podpisu APK v2 w bloku podpisywania APK. Jeśli blok v2 jest obecny, przejdź do kroku 3. W przeciwnym razie wróć do weryfikacji pakietu APK przy użyciu schematu v1 .
  3. Dla każdego signer w bloku APK Signature Scheme v2:
    1. Wybierz najsilniejszy obsługiwany signature algorithm ID podpisu spośród signatures . Kolejność siły zależy od każdej wersji wdrożenia/platformy.
    2. Zweryfikuj odpowiedni signature z signatures z 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. (Ma to na celu zapobieganie usuwaniu/dodawaniu podpisu).
    4. Oblicz skrót zawartości APK przy użyciu tego samego algorytmu skrótu, co algorytm skrótu używany przez algorytm podpisu.
    5. Sprawdź, czy obliczony digest jest identyczny z odpowiadającym mu skrótem ze digests .
    6. Sprawdź, czy SubjectPublicKeyInfo pierwszego certificate certificates jest identyczny z public key .
  4. Weryfikacja powiedzie się, jeśli znaleziono co najmniej jednego signer , a krok 3 powiódł się dla każdego znalezionego signer .

Uwaga : APK nie może być weryfikowany przy użyciu schematu v1, jeśli awaria wystąpi w kroku 3 lub 4.

Weryfikacja pakietu APK podpisanego w JAR (schemat v1)

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

  1. Każdy podpisujący jest reprezentowany przez wpis META-INF/<signer>.SF i META-INF/<signer>.(RSA|DSA|EC) JAR.
  2. <podpisujący>.(RSA|DSA|EC) to PKCS #7 CMS ContentInfo ze strukturą SignedData, którego podpis jest weryfikowany na podstawie pliku <podpisujący>.SF.
  3. Plik <signer>.SF zawiera skrót całego pliku META-INF/MANIFEST.MF oraz skróty każdej sekcji META-INF/MANIFEST.MF. Skrót całego pliku MANIFEST.MF jest weryfikowany. Jeśli to się nie powiedzie, zamiast tego zostanie zweryfikowany skrót każdej sekcji MANIFEST.MF.
  4. META-INF/MANIFEST.MF zawiera, dla każdego chronionego przez integralność wpisu JAR, odpowiednio nazwaną sekcję zawierającą skrót nieskompresowanej zawartości wpisu. Wszystkie te skróty są weryfikowane.
  5. Weryfikacja APK kończy się niepowodzeniem, 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 z ochroną integralności.