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 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:-
signature algorithm ID
(uint32) - (Längenpräfix)
digest
– siehe Integritätsgeschützte Inhalte
-
- Längenpräfixierte Sequenz von X.509-
certificates
:- X.509-
certificate
mit Längenpräfix (ASN.1 DER-Form)
- X.509-
- 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äfixierte Folge von längenpräfixierten
signatures
:-
signature algorithm ID
(uint32) -
signature
mit Längenpräfix übersigned 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:
- Inhalte der ZIP-Einträge (von Offset 0 bis zum Beginn des APK-Signaturblocks)
- APK-Signaturblock
- ZIP-Zentralverzeichnis
- ZIP-Ende des zentralen Verzeichnisses
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.
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.
Überprüfung des APK Signature Scheme v2
- Suchen Sie den APK-Signaturblock und überprüfen Sie Folgendes:
- Zwei Größenfelder des APK-Signaturblocks enthalten denselben Wert.
- Auf „ZIP Central Directory“ folgt unmittelbar der Eintrag „ZIP End of Central Directory“.
- Auf das ZIP-Ende des zentralen Verzeichnisses folgen keine weiteren Daten.
- 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.
- Für jeden
signer
im APK Signature Scheme v2-Block:- Wählen Sie aus
signatures
die stärkste unterstütztesignature algorithm ID
aus. Die Reihenfolge der Stärke hängt von der jeweiligen Implementierung/Plattformversion ab. - Überprüfen Sie die entsprechende
signature
vonsignatures
anhandsigned data
mithilfepublic key
. (Es ist jetzt sicher,signed data
zu analysieren.) - Stellen Sie sicher, dass die geordnete Liste der Signaturalgorithmus-IDs in
digests
undsignatures
identisch ist. (Dies dient dazu, das Entfernen/Hinzufügen von Signaturen zu verhindern.) - Berechnen Sie den Digest von APK-Inhalten mit demselben Digest-Algorithmus wie dem Digest-Algorithmus, der vom Signaturalgorithmus verwendet wird.
- Stellen Sie sicher, dass der berechnete Digest mit dem entsprechenden
digest
ausdigests
identisch ist. - Stellen Sie sicher
certificates
dass SubjectPublicKeyInfo des erstencertificate
mitpublic key
identisch ist.
- Wählen Sie aus
- Die Überprüfung ist erfolgreich, wenn mindestens ein
signer
gefunden wurde und Schritt 3 für jeden gefundenensigner
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:
- Jeder Unterzeichner wird durch einen JAR-Eintrag META-INF/<signer>.SF und META-INF/<signer>.(RSA|DSA|EC) 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 <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.
- 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.
- 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.