APK-Signaturschema v2

Das APK-Signaturschema v2 ist ein Signaturschema für ganze Dateien, das die Überprüfungsgeschwindigkeit erhöht und die Integritätsgarantien stärkt, indem es alle Änderungen an den geschützten Teilen des APK erkennt.

Beim Signieren mit dem APK-Signaturschema v2 wird unmittelbar vor dem Abschnitt „ZIP Central Directory“ ein APK -Signaturblock in die APK-Datei eingefügt. Innerhalb des APK-Signaturblocks werden v2-Signaturen und Identitätsinformationen des Unterzeichners in einem APK-Signaturschema v2-Block gespeichert.

APK vor und nach dem Signieren

Abbildung 1. APK vor und nach dem Signieren

Das APK-Signaturschema v2 wurde in Android 7.0 (Nougat) eingeführt. Um ein APK auf Android 6.0 (Marshmallow) und älteren Geräten installierbar zu machen, sollte das APK mit JAR-Signatur signiert werden, bevor es mit dem v2-Schema signiert wird.

APK-Signaturblock

Um die Abwärtskompatibilität mit dem v1-APK-Format aufrechtzuerhalten, werden v2- und neuere APK-Signaturen in einem APK-Signaturblock gespeichert, einem neuen Container, der zur Unterstützung des APK-Signaturschemas v2 eingeführt wurde. In einer APK-Datei befindet sich der APK-Signaturblock unmittelbar vor dem ZIP-Zentralverzeichnis, das sich am Ende der Datei befindet.

Der Block enthält ID-Wert-Paare, die so verpackt sind, dass es einfacher ist, den Block in der APK zu finden. Die v2-Signatur des APK wird als ID-Wert-Paar mit der ID 0x7109871a gespeichert.

Format

Das Format des APK-Signaturblocks ist wie folgt (alle numerischen Felder sind Little-Endian):

  • size of block in Bytes (ohne dieses Feld) (uint64)
  • Folge von ID-Wert-Paaren mit uint64-Längenpräfix:
    • ID (uint32)
    • value (variable-length: Länge des Paares - 4 Bytes)
  • size of block in Bytes – dasselbe wie das allererste Feld (uint64)
  • magic „APK Sig Block 42“ (16 Byte)

APK wird geparst, indem zuerst der Anfang des ZIP-Zentralverzeichnisses gefunden wird (indem der ZIP-Ende-Zentralverzeichnis-Datensatz am Ende der Datei gefunden wird und dann der Start-Offset des Zentralverzeichnisses aus dem Datensatz gelesen wird). Der magic Wert bietet eine schnelle Möglichkeit, um festzustellen, dass das, was dem zentralen Verzeichnis vorausgeht, wahrscheinlich der APK-Signaturblock ist. Die size of block zeigt dann effizient auf den Anfang des Blocks in der Datei.

ID-Wert-Paare mit unbekannten IDs sollten bei der Interpretation des Blocks ignoriert werden.

APK-Signaturschema v2-Block

APK wird von einem oder mehreren Unterzeichnern/Identitäten signiert, die jeweils durch einen Signaturschlüssel repräsentiert werden. Diese Informationen werden als APK-Signaturschema v2-Block gespeichert. Für jeden Unterzeichner werden die folgenden Informationen gespeichert:

  • (Signaturalgorithmus, Digest, Signatur) Tupel. Der Digest wird gespeichert, um die Signaturüberprüfung von der Integritätsprüfung des APK-Inhalts zu entkoppeln.
  • X.509-Zertifikatskette, die die Identität des Unterzeichners darstellt.
  • Zusätzliche Attribute als Schlüssel-Wert-Paare.

Für jeden Unterzeichner wird das APK mithilfe einer unterstützten Signatur aus der bereitgestellten Liste verifiziert. Signaturen mit unbekannten Signaturalgorithmen werden ignoriert. Es ist Sache jeder Implementierung, zu wählen, welche Signatur verwendet werden soll, wenn mehrere unterstützte Signaturen angetroffen werden. Dies ermöglicht die Einführung zukünftig stärkerer Signierverfahren rückwärtskompatibel. Der vorgeschlagene Ansatz besteht darin, die stärkste Signatur zu verifizieren.

Format

Der APK Signature Scheme v2 Block ist im APK Signing Block unter der ID 0x7109871a .

Das Format des APK-Signaturschema-v2-Blocks ist wie folgt (alle numerischen Werte sind Little-Endian, alle Felder mit Längenpräfix verwenden uint32 für die Länge):

  • Sequenz mit Längenpräfix von signer mit Längenpräfix :
    • Vorzeichenbehaftete signed data Längenpräfix:
      • Sequenz mit Längenpräfix von digests mit Längenpräfix:
      • Sequenz mit Längenpräfix von X.509- certificates :
        • X.509- certificate mit Längenpräfix (ASN.1 DER-Formular)
      • längenpräfixierte Folge von längenpräfixierten additional attributes :
        • ID (uint32)
        • value (variable-length: Länge des Zusatzattributs - 4 Bytes)
    • längenpräfixierte Folge von längenpräfixierten signatures :
      • signature algorithm ID (uint32)
      • signature mit Längenpräfix über signed data
    • public key mit Längenpräfix (SubjectPublicKeyInfo, ASN.1 DER-Formular)

Signaturalgorithmus-IDs

  • 0x0101 – RSASSA-PSS mit SHA2-256 Digest, SHA2-256 MGF1, 32 Byte Salt, Trailer: 0xbc
  • 0x0102 – RSASSA-PSS mit SHA2-512 Digest, SHA2-512 MGF1, 64 Byte Salt, Trailer: 0xbc
  • 0x0103 – RSASSA-PKCS1-v1_5 mit SHA2-256-Digest. Dies gilt für Build-Systeme, die deterministische Signaturen erfordern.
  • 0x0104 – RSASSA-PKCS1-v1_5 mit SHA2-512-Digest. Dies gilt für Build-Systeme, die deterministische Signaturen erfordern.
  • 0x0201 – ECDSA mit SHA2-256-Digest
  • 0x0202 – ECDSA mit SHA2-512-Digest
  • 0x0301 – DSA mit SHA2-256-Digest

Alle oben genannten Signaturalgorithmen werden von der Android-Plattform unterstützt. Signierwerkzeuge können eine Teilmenge der Algorithmen unterstützen.

Unterstützte Tastengrößen und EC-Kurven:

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

Integritätsgeschützte Inhalte

Zum Schutz von APK-Inhalten besteht ein APK aus vier Abschnitten:

  1. Inhalt von ZIP-Einträgen (von Offset 0 bis zum Beginn des APK-Signaturblocks)
  2. APK-Signaturblock
  3. ZIP-Zentralverzeichnis
  4. ZIP Ende des zentralen Verzeichnisses

APK-Abschnitte nach der Unterzeichnung

Abbildung 2. APK-Abschnitte nach dem Signieren

Das APK-Signaturschema v2 schützt die Integrität der Abschnitte 1, 3, 4 und die signed data Datenblöcke des APK-Signaturschema-v2-Blocks, der in Abschnitt 2 enthalten ist.

Die Integrität der Abschnitte 1, 3 und 4 wird durch einen oder mehrere Auszüge ihrer Inhalte geschützt, die in signed data Datenblöcken gespeichert sind, die wiederum durch eine oder mehrere Signaturen geschützt sind.

Der Digest über die Abschnitte 1, 3 und 4 wird wie folgt berechnet, ähnlich wie bei einem Merkle-Baum mit zwei Ebenen. Jeder Abschnitt ist in aufeinanderfolgende 1-MB-Blöcke (2 20 Byte) aufgeteilt. Der letzte Teil in jedem Abschnitt kann kürzer sein. Der Digest jedes Chunks wird über die Verkettung von Byte 0xa5 , der Länge des Chunks in Bytes (Little-Endian uint32) und dem Inhalt des Chunks berechnet. Der Digest der obersten Ebene wird über die Verkettung von Byte 0x5a , die Anzahl der Chunks (Little-Endian uint32) und die Verkettung der Digests der Chunks in der Reihenfolge berechnet, in der die Chunks im APK erscheinen. Der Digest wird in Chunks berechnet, um die Berechnung durch Parallelisierung zu beschleunigen.

APK-Digest

Abbildung 3. APK-Digest

Der Schutz von Abschnitt 4 (ZIP-Ende des Zentralverzeichnisses) wird durch den Abschnitt erschwert, der den Offset des ZIP-Zentralverzeichnisses enthält. Der Offset ändert sich, wenn sich die Größe des APK-Signaturblocks ändert, beispielsweise wenn eine neue Signatur hinzugefügt wird. Wenn also Digest über das ZIP-Ende des zentralen Verzeichnisses berechnet wird, muss das Feld, das den Offset des ZIP-Zentralverzeichnisses enthält, so behandelt werden, als ob es den Offset des APK-Signaturblocks enthält.

Rollback-Schutz

Ein Angreifer könnte versuchen, ein v2-signiertes APK auf Android-Plattformen, die die Verifizierung von v2-signierten APKs unterstützen, als v1-signiertes APK verifizieren zu lassen. Um diesen Angriff abzuschwächen, müssen v2-signierte APKs, die auch v1-signiert sind, ein X-Android-APK-Signed-Attribut im Hauptabschnitt ihrer META-INF/*.SF-Dateien enthalten. Der Wert des Attributs ist ein durch Kommas getrennter Satz von APK-Signaturschema-IDs (die ID dieses Schemas ist 2). Beim Verifizieren der v1-Signatur muss der APK-Verifizierer APKs zurückweisen, die keine Signatur für das APK-Signaturschema haben, das der Verifizierer aus diesem Satz bevorzugt (z. B. v2-Schema). Dieser Schutz beruht darauf, dass Inhalte von META-INF/*.SF-Dateien durch v1-Signaturen geschützt sind. Siehe den Abschnitt über JAR-signierte APK-Bestätigung .

Ein Angreifer könnte versuchen, stärkere Signaturen aus dem APK-Signaturschema v2-Block zu entfernen. Um diesen Angriff abzuschwächen, wird die Liste der Signaturalgorithmus-IDs, mit denen das APK signiert wurde, im signed data Datenblock gespeichert, der durch jede Signatur geschützt ist.

Überprüfung

In Android 7.0 und höher können APKs gemäß dem APK-Signaturschema v2+ oder JAR-Signatur (v1-Schema) verifiziert werden. Ältere Plattformen ignorieren v2-Signaturen und verifizieren nur v1-Signaturen.

APK-Signaturüberprüfungsprozess

Abbildung 4. Überprüfungsprozess der APK-Signatur (neue Schritte in Rot)

Überprüfung des APK-Signaturschemas v2

  1. Suchen Sie den APK-Signaturblock und überprüfen Sie Folgendes:
    1. Zwei Größenfelder des APK-Signaturblocks enthalten denselben Wert.
    2. ZIP Central Directory wird unmittelbar gefolgt von ZIP End of Central Directory-Datensatz.
    3. ZIP-Ende des zentralen Verzeichnisses folgen keine weiteren Daten.
  2. Suchen Sie den ersten APK-Signaturschema v2-Block innerhalb des APK-Signaturblocks. Wenn der v2-Block vorhanden ist, fahren Sie mit Schritt 3 fort. Andernfalls greifen Sie auf die Überprüfung des APK mit dem v1-Schema zurück.
  3. Für jeden signer im APK-Signaturschema v2-Block:
    1. Wählen Sie die stärkste unterstützte signature algorithm ID aus signatures . Die Reihenfolge der Stärke hängt von jeder Implementierungs-/Plattformversion ab.
    2. Verifizieren Sie die entsprechende signature von signatures gegen signed data unter Verwendung public key . (Es ist jetzt sicher, signed data zu parsen.)
    3. Stellen Sie sicher, dass die geordnete Liste der Signaturalgorithmus-IDs in digests und signatures identisch ist. (Dies soll das Entfernen/Hinzufügen von Signaturen verhindern.)
    4. Berechnen Sie den Digest von APK-Inhalten mit demselben Digest-Algorithmus wie der Digest-Algorithmus, der vom Signaturalgorithmus verwendet wird.
    5. Stellen Sie sicher, dass der berechnete Digest mit dem entsprechenden digest aus digests identisch ist.
    6. Stellen Sie sicher, dass SubjectPublicKeyInfo des ersten certificate der certificates mit dem public key identisch ist.
  4. Die Überprüfung ist erfolgreich, wenn mindestens ein signer gefunden wurde und Schritt 3 für jeden gefundenen signer erfolgreich war.

Hinweis : APK darf nicht mit dem v1-Schema verifiziert werden, wenn in Schritt 3 oder 4 ein Fehler auftritt.

JAR-signierte APK-Verifizierung (v1-Schema)

Das JAR-signierte APK ist ein standardmäßig signiertes JAR , das genau die Einträge enthalten muss, die in META-INF/MANIFEST.MF aufgeführt sind, und bei dem alle Einträge von denselben Unterzeichnern signiert sein müssen. Seine Integrität wird wie folgt überprüft:

  1. Jeder Unterzeichner wird durch einen JAR-Eintrag META-INF/<Unterzeichner>.SF und META-INF/<Unterzeichner>.(RSA|DSA|EC) dargestellt.
  2. <signer>.(RSA|DSA|EC) ist eine PKCS #7 CMS ContentInfo mit SignedData-Struktur, deren Signatur über die <signer>.SF-Datei verifiziert wird.
  3. Die Datei <signer>.SF enthält eine Gesamtdateiübersicht von META-INF/MANIFEST.MF und Übersichten von jedem Abschnitt von META-INF/MANIFEST.MF. Der Gesamtdatei-Digest von MANIFEST.MF wird verifiziert. Wenn dies fehlschlägt, wird stattdessen der Digest jedes MANIFEST.MF-Abschnitts überprüft.
  4. META-INF/MANIFEST.MF enthält für jeden integritätsgeschützten JAR-Eintrag einen entsprechend benannten Abschnitt, der den Digest des unkomprimierten Inhalts des Eintrags enthält. Alle diese Digests sind verifiziert.
  5. Die APK-Überprüfung schlägt fehl, wenn das APK JAR-Einträge enthält, die nicht in MANIFEST.MF aufgeführt und nicht Teil der JAR-Signatur sind.

Die Schutzkette ist somit <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> Inhalt jedes integritätsgeschützten JAR-Eintrags.