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.
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:signature algorithm ID
(uint32)- (length-prefixed)
digest
– siehe Inhalte mit Integritätsschutz
- Sequenz mit Längenpräfix von X.509-
certificates
:- X.509
certificate
mit Längenpräfix (ASN.1 DER-Format)
- X.509
- 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
- Sequenz mit Längenpräfix von
signatures
mit Längenpräfix:signature algorithm ID
(uint32)signature
mit Längenpräfix übersigned data
public key
mit Längenpräfix (SubjectPublicKeyInfo, ASN.1 DER-Formular)
- Längenvorangestellte
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:
- Inhalt der ZIP-Einträge (von Offset 0 bis zum Beginn des APK-Signaturblocks)
- Blockierung der APK-Signatur
- ZIP-Zentralverzeichnis
- ZIP End of Central Directory
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.
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 des APK-Signaturschemas v2
- Suchen Sie den Block für die APK-Signatur und prüfen Sie Folgendes:
- Zwei Größenfelder des APK-Signaturblocks enthalten denselben Wert.
- Dem ZIP Central Directory folgt unmittelbar der ZIP End of Central Directory-Eintrag.
- Auf das ZIP-Ende des Zentralverzeichnisses folgen keine weiteren Daten.
- 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.
- Für jede
signer
im Block „APK Signature Scheme v2“:- Wählen Sie die stärkste unterstützte
signature algorithm ID
aussignatures
aus. Die Reihenfolge der Stärke ist von der jeweiligen Implementierung/Plattformversion abhängig. - Prüfen Sie mit
public key
, ob der entsprechendesignature
aussignatures
mitsigned data
übereinstimmt. (signed data
kann jetzt sicher geparst werden.) - Prüfen Sie, ob die sortierte Liste der Signaturalgorithmus-IDs in
digests
undsignatures
identisch ist. Dadurch wird das Entfernen/Hinzufügen von Signaturen verhindert. - Berechnen Sie den Digest des APK-Inhalts mit demselben Digest-Algorithmus wie der vom Signaturalgorithmus verwendete Digest-Algorithmus.
- Prüfen Sie, ob der berechnete Digest mit dem entsprechenden
digest
ausdigests
identisch ist. - Prüfen Sie, ob SubjectPublicKeyInfo des ersten
certificate
voncertificates
mitpublic key
identisch ist.
- Wählen Sie die stärkste unterstützte
- Die Überprüfung ist erfolgreich, wenn mindestens eine
signer
gefunden wurde und Schritt 3 für jede gefundenesigner
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:
- Jeder Unterzeichner wird durch einen META-INF/<signer>.SF- und META-INF/<signer>.(RSA|DSA|EC)-JAR-Eintrag dargestellt.
- <signer>.(RSA|DSA|EC) ist eine PKCS #7 CMS ContentInfo mit SignedData-Struktur, deren Signatur über die Datei <signer>.SF überprüft wird.
- 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.
- 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.
- 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.