Schéma de signature APK v2

APK Signature Scheme v2 est un schéma de signature de l'intégralité du fichier qui augmente la vitesse de vérification et renforce les garanties d'intégrité en détectant toute modification apportée aux parties protégées de l'APK.

La signature à l'aide du schéma de signature APK v2 insère un bloc de signature APK dans le fichier APK, juste avant la section ZIP Central Directory. Dans le bloc de signature de l'APK, les signatures v2 et les informations d'identité du signataire sont stockées dans un bloc APK Signature Scheme v2.

APK avant et après la signature

Figure 1 : APK avant et après la signature

APK Signature Scheme v2 a été introduit dans Android 7.0 (Nougat). Pour qu'un APK puisse être installé sur Android 6.0 (Marshmallow) et les appareils plus anciens, il doit être signé à l'aide de la signature JAR avant d'être signé avec le schéma v2.

Bloc de signature d'APK

Pour assurer la rétrocompatibilité avec le format APK v1, les signatures APK v2 et ultérieures sont stockées dans un bloc de signature d'APK, un nouveau conteneur introduit pour prendre en charge le schéma de signature d'APK v2. Dans un fichier APK, le bloc de signature de l'APK se trouve immédiatement avant le répertoire central ZIP, qui se trouve à la fin du fichier.

Le bloc contient des paires d'ID-valeur encapsulées de manière à faciliter la localisation du bloc dans l'APK. La signature v2 de l'APK est stockée sous la forme d'une paire d'ID-valeur avec l'ID 0x7109871a.

Format

Le format du bloc de signature de l'APK est le suivant (tous les champs numériques sont en ordre petit-endian):

  • size of block en octets (hors champ) (uint64)
  • Séquence de paires ID/valeur avec préfixe uint64-length :
    • ID (uint32)
    • value (longueur variable: longueur de la paire - 4 octets)
  • size of block en octets (identique au tout premier champ (uint64))
  • magic "APK Sig Block 42" (16 octets)

L'APK est analysé en trouvant d'abord le début du répertoire central ZIP (en trouvant l'enregistrement de fin ZIP du répertoire central à la fin du fichier, puis en lisant le décalage de début du répertoire central à partir de l'enregistrement). La valeur magic permet de déterminer rapidement que ce qui précède le répertoire central est probablement le bloc de signature de l'APK. La valeur size of block pointe ensuite efficacement sur le début du bloc dans le fichier.

Les paires d'ID-valeurs avec des ID inconnus doivent être ignorées lors de l'interprétation du bloc.

Bloc APK Signature Scheme v2

L'APK est signé par un ou plusieurs signataires/identités, chacun représenté par une clé de signature. Ces informations sont stockées sous forme de bloc APK Signature Scheme v2. Pour chaque signataire, les informations suivantes sont stockées:

  • (algorithme de signature, condensé, signature). Le récapitulatif est stocké pour dissocier la validation de la signature de la vérification de l'intégrité du contenu de l'APK.
  • Chaîne de certificats X.509 représentant l'identité du signataire.
  • Attributs supplémentaires sous forme de paires clé-valeur.

Pour chaque signataire, l'APK est validé à l'aide d'une signature compatible de la liste fournie. Les signatures avec des algorithmes de signature inconnus sont ignorées. Il appartient à chaque implémentation de choisir la signature à utiliser lorsqu'il existe plusieurs signatures compatibles. Cela permet d'introduire à l'avenir des méthodes de signature plus robustes et rétrocompatibles. L'approche suggérée consiste à vérifier la signature la plus forte.

Format

Le bloc APK Signature Scheme v2 est stocké dans le bloc de signature APK sous l'ID 0x7109871a.

Le format du bloc v2 du schéma de signature de l'APK est le suivant (toutes les valeurs numériques sont few-endian, tous les champs avec préfixe de longueur utilisent uint32 pour la longueur):

  • Séquence avec préfixe de longueur de signer avec préfixe de longueur :
    • signed data avec préfixe de longueur :
      • Séquence préfixée par la longueur d'digests préfixée par la longueur :
      • Séquence X.509 certificates avec préfixe de longueur :
        • certificate X.509 avec préfixe de longueur (format ASN.1 DER)
      • Séquence préfixée par la longueur d'additional attributes préfixée par la longueur :
        • ID (uint32)
        • value (longueur de la variable: longueur de l'attribut supplémentaire - 4 octets)
    • Séquence avec un préfixe de longueur de signatures avec le préfixe de longueur :
      • signature algorithm ID (uint32)
      • signature avec préfixe de longueur sur signed data
    • public key avec préfixe de longueur (SubjectPublicKeyInfo, format ASN.1 DER)

ID d'algorithme de signature

  • 0x0101 : RSASSA-PSS avec condensé SHA2-256, MGF1 SHA2-256, 32 octets de sel, postscript : 0xbc
  • 0x0102 : RSASSA-PSS avec condensé SHA2-512, MGF1 SHA2-512, 64 octets de sel, panneau : 0xbc
  • 0x0103 : RSASSA-PKCS1-v1_5 avec condensé SHA2-256 Il s'agit des systèmes de compilation qui nécessitent des signatures déterministes.
  • 0x0104 : RSASSA-PKCS1-v1_5 avec condensé SHA2-512. Il s'agit des systèmes de compilation qui nécessitent des signatures déterministes.
  • 0x0201 : ECDSA avec condensé SHA2-256
  • 0x0202 : ECDSA avec condensé SHA2-512
  • 0x0301 : DSA avec condensé SHA2-256

Tous les algorithmes de signature ci-dessus sont compatibles avec la plate-forme Android. Les outils de signature peuvent prendre en charge un sous-ensemble des algorithmes.

Tailles de clés et courbes elliptiques compatibles:

  • RSA: 1 024, 2 048, 4 096, 8 192, 16 384
  • EC: NIST P-256, P-384, P-521
  • DSA: 1 024, 2 048, 3 072

Contenus protégés par l'intégrité

Pour protéger le contenu d'un APK, celui-ci se compose de quatre sections:

  1. Contenu des entrées ZIP (de l'offset 0 jusqu'au début du bloc de signature de l'APK)
  2. Bloc de signature de l'APK
  3. Répertoire central ZIP
  4. Fin ZIP du répertoire central

Sections de l'APK après signature

Figure 2. Sections de l'APK après signature

Le schéma de signature APK v2 protège l'intégrité des sections 1, 3 et 4, ainsi que les blocs signed data du bloc v2 du schéma de signature APK contenu dans la section 2.

L'intégrité des sections 1, 3 et 4 est protégée par un ou plusieurs récapitulatifs de leur contenu stockés dans des blocs signed data, qui sont à leur tour protégés par une ou plusieurs signatures.

Le récapitulatif des sections 1, 3 et 4 est calculé comme suit, comme un arbre de Merkle à deux niveaux. Chaque section est divisée en fragments consécutifs de 1 Mo (220 octets). Le dernier segment de chaque section peut être plus court. Le récapitulatif de chaque segment est calculé sur la concaténation de l'octet 0xa5, de la longueur du segment en octets (uint32 little-endian) et du contenu du segment. Le récapitulatif de niveau supérieur est calculé sur la concaténation de l'octet 0x5a, du nombre de blocs (uint32 little-endian) et de la concaténation des récapitulatifs des blocs dans l'ordre dans lequel ils apparaissent dans l'APK. Le récapitulatif est calculé par blocs pour accélérer le calcul en le parallélisant.

Récapitulatif APK

Figure 3. Récapitulatif APK

La protection de la section 4 (fin ZIP du répertoire central) est compliquée par la section contenant le décalage du répertoire ZIP Central. Le décalage change lorsque la taille du bloc de signature de l'APK change, par exemple, lorsqu'une nouvelle signature est ajoutée. Par conséquent, lors du calcul du récapitulatif sur la fin du répertoire central ZIP, le champ contenant le décalage du répertoire central ZIP doit être traité comme contenant le décalage du bloc de signature de l'APK.

Protections contre le rollback

Un pirate informatique peut tenter de faire valider un APK signé en version 2 comme un APK signé en version 1 sur les plates-formes Android compatibles avec la validation des APK signés en version 2. Pour atténuer cette attaque, les APK signés en version 2 qui sont également signés en version 1 doivent contenir un attribut X-Android-APK-Signed dans la section principale de leurs fichiers META-INF/*.SF. La valeur de l'attribut est un ensemble d'ID de schéma de signature APK séparés par une virgule (l'ID de ce schéma est 2). Lors de la validation de la signature v1, le vérificateur d'APK doit refuser les APK qui ne disposent pas d'une signature pour le schéma de signature d'APK préféré par le vérificateur dans cet ensemble (par exemple, le schéma v2). Cette protection repose sur le fait que les contenus des fichiers META-INF/*.SF sont protégés par des signatures v1. Consultez la section sur la validation des APK signés JAR.

Un pirate informatique peut tenter de supprimer les signatures plus sécurisées du bloc APK Signature Scheme v2. Pour limiter cette attaque, la liste des ID d'algorithme de signature avec lesquels l'APK a été signé est stockée dans le bloc signed data, qui est protégé par chaque signature.

Validation

Dans Android 7.0 et versions ultérieures, les APK peuvent être vérifiés selon le schéma de signature APK v2+ ou la signature JAR (schéma v1). Les plates-formes plus anciennes ignorent les signatures v2 et ne valident que les signatures v1.

Processus de validation de la signature d'un APK

Figure 4. Processus de validation de la signature de l'APK (nouvelles étapes en rouge)

Validation du schéma de signature APK v2

  1. Recherchez le bloc de signature de l'APK et vérifiez les points suivants :
    1. Deux champs de taille du bloc de signature de l'APK contiennent la même valeur.
    2. L'enregistrement ZIP End of Central Directory est immédiatement suivi par l'enregistrement ZIP Central Directory.
    3. La fin du répertoire central ZIP n'est pas suivie d'autres données.
  2. Recherchez le premier bloc APK Signature Scheme v2 dans le bloc de signature APK. Si le bloc v2 est présent, passez à l'étape 3. Sinon, vérifiez l'APK à l'aide du schéma v1.
  3. Pour chaque signer du bloc APK Signature Scheme v2 :
    1. Choisissez le signature algorithm ID le plus puissant compatible avec signatures. L'ordre de force dépend de chaque implémentation/version de plate-forme.
    2. Vérifiez le signature correspondant de signatures avec signed data à l'aide de public key. (Vous pouvez désormais analyser signed data en toute sécurité.)
    3. Vérifiez que la liste ordonnée des ID d'algorithme de signature dans digests et signatures est identique. (Cela permet d'éviter la suppression/l'ajout de signature.)
    4. Calculez le condensé du contenu de l'APK à l'aide du même algorithme de condensé que celui utilisé par l'algorithme de signature.
    5. Vérifiez que le récapitulatif calculé est identique à l'digest correspondant de digests.
    6. Vérifiez que SubjectPublicKeyInfo de la première certificate de certificates est identique à public key.
  4. La validation aboutit si au moins un élément signer a été trouvé et que l'étape 3 a réussi pour chaque signer trouvé.

Remarque: L'APK ne doit pas être validé à l'aide du schéma v1 si une erreur se produit à l'étape 3 ou 4.

Vérification des APK signés avec JAR (schéma v1)

L'APK signé par JAR est un JAR standard signé qui doit contenir exactement les entrées listées dans META-INF/MANIFEST.MF et où toutes les entrées doivent être signées par le même ensemble de signataires. Son intégrité est vérifiée comme suit:

  1. Chaque signataire est représenté par une entrée JAR META-INF/<signer>.SF et META-INF/<signer>.(RSA|DSA|EC).
  2. <signer>.(RSA|DSA|EC) est un ContentInfo PKCS #7 CMS avec une structure SignedData dont la signature est validée sur le fichier <signer>.SF.
  3. Le fichier .SF <signer> contient un condensé complet de META-INF/MANIFEST.MF, ainsi que des condensés de chaque section de META-INF/MANIFEST.MF. Le récapitulatif de l'ensemble du fichier MANIFEST.MF est validé. En cas d'échec, le condensé de chaque section MANIFEST.MF est vérifié à la place.
  4. META-INF/MANIFEST.MF contient, pour chaque entrée JAR protégée par l'intégrité, une section correspondante contenant le récapitulatif du contenu non compressé de l'entrée. Tous ces récapitulatifs sont vérifiés.
  5. La validation de l'APK échoue si l'APK contient des entrées JAR qui ne sont pas listées dans le fichier MANIFEST.MF et qui ne font pas partie de la signature JAR.

La chaîne de protection est donc <signer>.(RSA|DSA|EC) -> <signer>.SF -> MANIFEST.MF -> contenu de chaque entrée JAR protégée par l'intégrité.