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.
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:-
signature algorithm ID
(uint32) - (mit Längenpräfix)
digest
– siehe Integritätsgeschützte Inhalte
-
- Sequenz mit Längenpräfix von X.509-
certificates
:- X.509-
certificate
mit Längenpräfix (ASN.1 DER-Formular)
- X.509-
- längenpräfixierte Folge von längenpräfixierten
additional attributes
:-
ID
(uint32) -
value
(variable-length: Länge des Zusatzattributs - 4 Bytes)
-
- Sequenz mit Längenpräfix 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-Formular)
- Vorzeichenbehaftete
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:
- Inhalt von ZIP-Einträgen (von Offset 0 bis zum Beginn des APK-Signaturblocks)
- APK-Signaturblock
- ZIP-Zentralverzeichnis
- ZIP Ende des zentralen Verzeichnisses
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.
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.
Abbildung 4. Überprüfungsprozess der APK-Signatur (neue Schritte in Rot)
Überprüfung des APK-Signaturschemas v2
- Suchen Sie den APK-Signaturblock und überprüfen Sie Folgendes:
- Zwei Größenfelder des APK-Signaturblocks enthalten denselben Wert.
- ZIP Central Directory wird unmittelbar gefolgt von ZIP End of Central Directory-Datensatz.
- ZIP-Ende des zentralen Verzeichnisses folgen keine weiteren Daten.
- 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.
- Für jeden
signer
im APK-Signaturschema v2-Block:- Wählen Sie die stärkste unterstützte
signature algorithm ID
aussignatures
. Die Reihenfolge der Stärke hängt von jeder Implementierungs-/Plattformversion ab. - Verifizieren Sie die entsprechende
signature
vonsignatures
gegensigned data
unter Verwendungpublic key
. (Es ist jetzt sicher,signed data
zu parsen.) - Stellen Sie sicher, dass die geordnete Liste der Signaturalgorithmus-IDs in
digests
undsignatures
identisch ist. (Dies soll das Entfernen/Hinzufügen von Signaturen verhindern.) - Berechnen Sie den Digest von APK-Inhalten mit demselben Digest-Algorithmus wie der Digest-Algorithmus, der vom Signaturalgorithmus verwendet wird.
- Stellen Sie sicher, dass der berechnete Digest mit dem entsprechenden
digest
ausdigests
identisch ist. - Stellen Sie sicher, dass SubjectPublicKeyInfo des ersten
certificate
dercertificates
mit dempublic key
identisch ist.
- Wählen Sie die stärkste unterstützte
- 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 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:
- Jeder Unterzeichner wird durch einen JAR-Eintrag META-INF/<Unterzeichner>.SF und META-INF/<Unterzeichner>.(RSA|DSA|EC) dargestellt.
- <signer>.(RSA|DSA|EC) ist eine PKCS #7 CMS ContentInfo mit SignedData-Struktur, deren Signatur über die <signer>.SF-Datei verifiziert wird.
- 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.
- 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 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.