APK-Signaturschema v2

APK Signature Scheme v2 ist ein Signaturschema für die gesamte Datei, das die Verifizierungsgeschwindigkeit erhöht und die Integritätsgarantien stärkt , indem es alle Änderungen an den geschützten Teilen der APK erkennt.

Beim Signieren mit APK Signature Scheme v2 wird unmittelbar vor dem Abschnitt „ZIP Central Directory“ ein APK-Signaturblock in die APK-Datei eingefügt. Im APK-Signaturblock werden v2-Signaturen und Unterzeichneridentitätsinformationen in einem APK-Signaturschema v2-Block gespeichert.

APK vor und nach dem Signieren

Abbildung 1. APK vor und nach dem Signieren

APK Signature Scheme v2 wurde in Android 7.0 (Nougat) eingeführt. Um eine APK auf Android 6.0 (Marshmallow) und älteren Geräten installierbar zu machen, sollte die APK mit der JAR-Signatur signiert werden, bevor sie mit dem v2-Schema signiert wird.

APK-Signaturblock

Um die Abwärtskompatibilität mit dem APK-Format v1 aufrechtzuerhalten, werden APK-Signaturen der Version 2 und neuer 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 das Auffinden des Blocks im APK erleichtert wird. 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)
  • Reihenfolge der ID-Wert-Paare mit uint64-Länge und Präfix:
    • ID (uint32)
    • value (Variablenlänge: Länge des Paares – 4 Bytes)
  • size of block in Bytes – identisch mit dem allerersten Feld (uint64)
  • magic „APK Sig Block 42“ (16 Bytes)

APK wird geparst, indem zunächst der Anfang des ZIP-Zentralverzeichnisses ermittelt wird (indem der Datensatz „ZIP-Ende des Zentralverzeichnisses“ am Ende der Datei ermittelt und dann der Startoffset des Zentralverzeichnisses aus dem Datensatz gelesen wird). Der magic Wert bietet eine schnelle Möglichkeit, festzustellen, dass es sich bei dem, was dem zentralen Verzeichnis vorausgeht, wahrscheinlich um den APK-Signaturblock handelt. 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 Signature Scheme v2 Block

APK wird von einem oder mehreren Unterzeichnern/Identitäten signiert, die jeweils durch einen Signaturschlüssel dargestellt werden. Diese Informationen werden als APK Signature Scheme v2 Block gespeichert. Für jeden Unterzeichner werden folgende 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 die APK anhand einer unterstützten Signatur aus der bereitgestellten Liste überprüft. Signaturen mit unbekannten Signaturalgorithmen werden ignoriert. Es liegt an jeder Implementierung, zu entscheiden, welche Signatur verwendet werden soll, wenn mehrere unterstützte Signaturen auftreten. Dies ermöglicht die Einführung stärkerer Signierungsmethoden in der Zukunft auf abwärtskompatible Weise. Der vorgeschlagene Ansatz besteht darin, die stärkste Signatur zu überprüfen.

Format

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

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

  • Längenpräfixierte Sequenz des längenpräfixierten signer :
    • signed data mit Längenpräfix:
      • Längenpräfixierte Folge von digests mit Längenpräfix:
      • Längenpräfixierte Sequenz von X.509- certificates :
        • X.509- certificate mit Längenpräfix (ASN.1 DER-Form)
      • Längenpräfixierte Folge von längenpräfixierten additional attributes :
        • ID (uint32)
        • value (Variablenlänge: Länge des zusätzlichen Attributs - 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-Form)

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. Signierungstools unterstützen möglicherweise eine Teilmenge der Algorithmen.

Unterstützte Schlüsselgröß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. Inhalte der ZIP-Einträge (von Offset 0 bis zum Beginn des APK-Signaturblocks)
  2. APK-Signaturblock
  3. ZIP-Zentralverzeichnis
  4. ZIP-Ende des zentralen Verzeichnisses

APK-Abschnitte nach dem Signieren

Abbildung 2. APK-Abschnitte nach dem Signieren

APK Signature Scheme v2 schützt die Integrität der Abschnitte 1, 3, 4 und der signed data des in Abschnitt 2 enthaltenen APK Signature Scheme v2-Blocks.

Die Integrität der Abschnitte 1, 3 und 4 wird durch einen oder mehrere Zusammenfassungen 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 einem zweistufigen Merkle-Baum . Jeder Abschnitt ist in aufeinanderfolgende 1 MB (2 20 Byte) große Blöcke aufgeteilt. Der letzte Abschnitt in jedem Abschnitt kann kürzer sein. Der Digest jedes Blocks wird über die Verkettung von Byte 0xa5 , der Länge des Blocks in Bytes (Little-Endian uint32) und dem Inhalt des Blocks 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-Übersicht

Abbildung 3. APK-Digest

Der Schutz von Abschnitt 4 (ZIP-Ende des zentralen Verzeichnisses) 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 der 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 Überprüfung v2-signierter APK unterstützen, als v1-signiertes APK verifizieren zu lassen. Um diesen Angriff abzuwehren, müssen v2-signierte APKs, die auch v1-signiert sind, im Hauptabschnitt ihrer META-INF/*.SF-Dateien ein X-Android-APK-Signed-Attribut enthalten. Der Wert des Attributs ist ein durch Kommas getrennter Satz von APK-Signaturschema-IDs (die ID dieses Schemas ist 2). Bei der Überprüfung der v1-Signatur muss der APK-Verifizierer APKs ablehnen, die keine Signatur für das APK-Signaturschema haben, das der Verifizierer aus diesem Satz bevorzugt (z. B. v2-Schema). Dieser Schutz beruht auf der Tatsache, dass Inhalte von META-INF/*.SF-Dateien durch v1-Signaturen geschützt sind. Weitere Informationen finden Sie im Abschnitt zur JAR-signierten APK-Verifizierung .

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

Überprüfung

In Android 7.0 und höher können APKs gemäß dem APK Signature Scheme v2+ oder der JAR-Signierung (v1-Schema) überprüft werden. Ältere Plattformen ignorieren v2-Signaturen und überprüfen nur v1-Signaturen.

APK-Signaturüberprüfungsprozess

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

Überprüfung des APK Signature Scheme v2

  1. Suchen Sie den APK-Signaturblock und überprüfen Sie Folgendes:
    1. Zwei Größenfelder des APK-Signaturblocks enthalten denselben Wert.
    2. Auf „ZIP Central Directory“ folgt unmittelbar der Eintrag „ZIP End of Central Directory“.
    3. Auf das ZIP-Ende des zentralen Verzeichnisses folgen keine weiteren Daten.
  2. Suchen Sie den ersten APK Signature Scheme v2-Block im APK-Signaturblock. Wenn der v2-Block vorhanden ist, fahren Sie mit Schritt 3 fort. Andernfalls greifen Sie auf die Überprüfung des APK mithilfe des v1-Schemas zurück.
  3. Für jeden signer im APK Signature Scheme v2-Block:
    1. Wählen Sie aus signatures die stärkste unterstützte signature algorithm ID aus. Die Reihenfolge der Stärke hängt von der jeweiligen Implementierung/Plattformversion ab.
    2. Überprüfen Sie die entsprechende signature von signatures anhand signed data mithilfe public key . (Es ist jetzt sicher, signed data zu analysieren.)
    3. Stellen Sie sicher, dass die geordnete Liste der Signaturalgorithmus-IDs in digests und signatures identisch ist. (Dies dient dazu, das Entfernen/Hinzufügen von Signaturen zu verhindern.)
    4. Berechnen Sie den Digest von APK-Inhalten mit demselben Digest-Algorithmus wie dem 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 certificates dass SubjectPublicKeyInfo des ersten certificate mit 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 in META-INF/MANIFEST.MF aufgeführten Einträge enthalten muss 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/<signer>.SF und META-INF/<signer>.(RSA|DSA|EC) 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 <Unterzeichner>.SF enthält einen Auszug der gesamten Datei von META-INF/MANIFEST.MF und Auszüge aller Abschnitte von META-INF/MANIFEST.MF. Der gesamte Datei-Digest von MANIFEST.MF wird überprüft. 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 die APK JAR-Einträge enthält, die nicht in MANIFEST.MF aufgeführt sind 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.