APK-Signaturschema v2

Das APK-Signaturschema v2 ist ein Signaturschema für die gesamte Datei, das die Überprüfungsgeschwindigkeit erhöht und die Integrität garantiert, indem alle Änderungen an den geschützten Teilen der APK erkannt werden.

Bei der Signatur mit dem APK-Signaturschema v2 wird direkt vor dem ZIP Central Directory-Abschnitt ein APK-Signaturblock in die APK-Datei eingefügt. Innerhalb des APK-Signaturblocks werden V2-Signaturen und Informationen zur Identität des Unterzeichners in einem APK-Signaturschema v2-Block gespeichert.

APK vor und nach dem Signieren

Abbildung 1. APK vor und nach der Signatur

Das APK-Signaturschema v2 wurde mit Android 7.0 (Nougat) eingeführt. Damit eine APK auf Android 6.0 (Marshmallow) und älteren Geräten installiert werden kann, muss sie mit der JAR-Signatur signiert werden, bevor sie mit dem V2-Schema signiert wird.

Blockierung der APK-Signatur

Zur Aufrechterhaltung der Abwärtskompatibilität mit dem APK-Format v1 werden APK-Signaturen der Version 2 und höher in einem APK-Signaturblock gespeichert. Dies ist ein neuer Container, der zur Unterstützung des APK-Signaturschemas v2 eingeführt wurde. In einer APK-Datei befindet sich der APK-Signaturblock direkt vor dem ZIP Central Directory, das sich am Ende der Datei befindet.

Der Block enthält ID-Wert-Paare, die so umschlossen sind, dass der Block im APK leichter zu finden ist. Die V2-Signatur des APK wird als ID-Wertpaar mit der ID 0x7109871a gespeichert.

Formatieren

Das Format des APK-Signaturblocks sieht so aus (alle numerischen Felder sind im Little-Endian-Format):

  • size of block in Byte (ohne dieses Feld) (uint64)
  • Sequenz von ID-Wert-Paaren mit uint64-Längenpräfix:
    • ID (uint32)
    • value (variable Länge: Länge des Paars – 4 Byte)
  • size of block in Byte – wie im ersten Feld (uint64)
  • magic „APK Sig Block 42“ (16 Byte)

Das APK wird analysiert, indem zuerst der Anfang des ZIP-Zentralverzeichnisses ermittelt wird. Dazu wird der ZIP-Eintrags-Ende-Zentralverzeichnis-Eintrag am Ende der Datei gefunden und dann der Startoffset des Zentralverzeichnisses aus dem Eintrag gelesen. Mit dem Wert magic lässt sich schnell feststellen, ob das, was vor dem Central Directory liegt, der APK-Signaturblock ist. Der Wert size of block verweist dann effizient auf den Anfang des Blocks in der Datei.

ID-Wert-Paare mit unbekannten IDs sollten bei der Blockauswertung ignoriert werden.

Blockierung des APK-Signaturschemas v2

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

  • (Signaturalgorithmus, Digest, Signatur). Der Hashwert wird gespeichert, um die Signaturprü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 mit einer unterstützten Signatur aus der bereitgestellten Liste überprüft. Signaturen mit unbekannten Signaturalgorithmen werden ignoriert. Bei mehreren unterstützten Signaturen muss die jeweilige Implementierung auswählen, welche Signatur verwendet werden soll. So können in Zukunft rückwärtskompatible, leistungsfähigere Signaturmethoden eingeführt werden. Wir empfehlen, die stärkste Signatur zu prüfen.

Formatieren

Der Block „APK-Signaturschema v2“ wird im Block „APK-Signatur“ unter der ID 0x7109871a gespeichert.

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

  • Sequenz mit Längenpräfix von signer mit Längenpräfix:
    • Längenvorangestellte signed data:
      • 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-Format)
      • Sequenz mit Längenpräfix von additional attributes mit Längenpräfix:
        • ID (uint32)
        • value (variable Länge: Länge des zusätzlichen Attributs – 4 Byte)
    • Sequenz mit Längenpräfix von signatures mit Längenpräfix:
      • 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. Signaturtools können nur einen Teil der Algorithmen unterstützen.

Unterstützte Schlüsselgrößen und EC-Kurven:

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

Integritätsgeschützte Inhalte

Zum Schutz des APK-Inhalts besteht ein APK aus vier Abschnitten:

  1. Inhalt der ZIP-Einträge (von Offset 0 bis zum Beginn des APK-Signaturblocks)
  2. Blockierung der APK-Signatur
  3. ZIP-Zentralverzeichnis
  4. ZIP End of Central Directory

APK-Abschnitte nach der Signatur

Abbildung 2. APK-Bereiche nach dem Signieren

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

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

Der Digest in den Abschnitten 1, 3 und 4 wird so berechnet, ähnlich wie bei einem Merkle-Baum mit zwei Ebenen. Jeder Abschnitt wird in aufeinanderfolgende Blöcke von 1 MB (220 Byte) aufgeteilt. Der letzte Block in jedem Abschnitt kann kürzer sein. Der Digest jedes Blocks wird anhand der Verkettung von Byte 0xa5, der Länge des Chunks in Byte (Little-Endian uint32) und dem Inhalt des Blocks berechnet. Der Digest der obersten Ebene wird aus der Konkatenierung von Byte 0x5a, der Anzahl der Blöcke (Little-Endian-uint32) und der Konkatenierung der Digests der Blöcke in der Reihenfolge berechnet, in der sie im APK erscheinen. Der Hashwert wird in Form von Chunks berechnet, um die Berechnung durch Parallelisierung zu beschleunigen.

APK-Benachrichtigung

Abbildung 3 APK-Digest

Der Schutz von Abschnitt 4 (ZIP End of Central Directory) ist durch den Abschnitt mit dem Offset des ZIP Central Directory erschwert. Der Offset ändert sich, wenn sich die Größe des APK-Signaturblocks ändert, z. B. wenn eine neue Signatur hinzugefügt wird. Bei der Berechnung des Digests über das ZIP End of Central Directory muss das Feld mit dem Offset des ZIP Central Directory als Offset des APK-Signaturblocks behandelt werden.

Rollback-Schutz

Ein Angreifer könnte versuchen, ein mit v2 signiertes APK auf Android-Plattformen, die die Überprüfung von mit v2 signierten APKs unterstützen, als mit v1 signiertes APK zu verifizieren. Um diesen Angriff abzuschwächen, müssen APKs, die sowohl mit V2 als auch mit V1 signiert sind, im Hauptabschnitt ihrer META-INF/*.SF-Dateien das Attribut „X-Android-APK-Signed“ enthalten. Der Wert des Attributs ist eine durch Kommas getrennte Liste von APK-Signaturschema-IDs (die ID dieses Schemas ist 2). Beim Verifizieren der v1-Signatur muss die APK-Prüfung APKs ablehnen, die keine Signatur für das APK-Signaturschema haben, das der Prüfer aus diesem Satz bevorzugt (z.B. v2-Schema). Dieser Schutz beruht darauf, dass der Inhalt von META-INF/*.SF-Dateien durch V1-Signaturen geschützt ist. Weitere Informationen finden Sie im Abschnitt JAR-signierte APKs überprüfen.

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

Bestätigung

Unter Android 7.0 und höher können APKs gemäß dem APK-Signaturschema v2 oder höher oder der JAR-Signatur (Schema v1) überprüft werden. Ältere Plattformen ignorieren V2-Signaturen und prüfen nur V1-Signaturen.

Überprüfung der APK-Signatur

Abbildung 4 Prozess zur Überprüfung der APK-Signatur (neue Schritte in Rot)

Überprüfung des APK-Signaturschemas v2

  1. Suchen Sie den Block für die APK-Signatur und prüfen Sie Folgendes:
    1. Zwei Größenfelder des APK-Signaturblocks enthalten denselben Wert.
    2. Dem ZIP Central Directory folgt unmittelbar der ZIP End of Central Directory-Eintrag.
    3. Auf das ZIP-Ende des Zentralverzeichnisses folgen keine weiteren Daten.
  2. Suchen Sie im Block für die APK-Signatur nach dem ersten Block für das APK-Signaturschema v2. Wenn der v2-Block vorhanden ist, fahren Sie mit Schritt 3 fort. Andernfalls können Sie die APK-Signatur mit dem V1-Schema überprüfen.
  3. Für jede signer im Block „APK Signature Scheme v2“:
    1. Wählen Sie die stärkste unterstützte signature algorithm ID aus signatures aus. Die Reihenfolge der Stärke ist von der jeweiligen Implementierung/Plattformversion abhängig.
    2. Prüfen Sie mit public key, ob der entsprechende signature aus signatures mit signed data übereinstimmt. (signed data kann jetzt sicher geparst werden.)
    3. Prüfen Sie, ob die sortierte Liste der Signaturalgorithmus-IDs in digests und signatures identisch ist. Dadurch wird das Entfernen/Hinzufügen von Signaturen verhindert.
    4. Berechnen Sie den Digest des APK-Inhalts mit demselben Digest-Algorithmus wie der vom Signaturalgorithmus verwendete Digest-Algorithmus.
    5. Prüfen Sie, ob der berechnete Digest mit dem entsprechenden digest aus digests identisch ist.
    6. Prüfen Sie, ob SubjectPublicKeyInfo des ersten certificate von certificates mit public key identisch ist.
  4. Die Überprüfung ist erfolgreich, wenn mindestens eine signer gefunden wurde und Schritt 3 für jede gefundene signer erfolgreich war.

Hinweis: Das APK darf nicht mit dem V1-Schema überprüft werden, wenn in Schritt 3 oder 4 ein Fehler auftritt.

Überprüfung von JAR-signierten APKs (Schema v1)

Das JAR-signierte APK ist eine standardmäßig signierte JAR-Datei, die genau die in META-INF/MANIFEST.MF aufgeführten Einträge enthalten muss und alle Einträge von denselben Signaturern signiert werden müssen. Die Integrität wird so überprüft:

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

Die Schutzkette besteht also aus <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> Inhalt jedes vor Manipulation geschützten JAR-Eintrags.