Signer les builds en vue de leur publication

Les images de l'OS Android utilisent des signatures cryptographiques à deux endroits :

  1. Chaque fichier .apk dans l'image doit être signé. Le gestionnaire de paquets d'Android utilise une signature .apk de deux manières :
    • Lorsqu'une application est remplacée, elle doit être signée avec la même clé que la ancienne application afin d'accéder aux données de l'ancienne application. Cela est vrai à la fois pour la mise à jour des applications utilisateur en écrasant .apk et pour le forçage d'une application système avec une version plus récente installée sous /data.
    • Si deux applications ou plus souhaitent partager un ID utilisateur (afin de pouvoir partager données, etc.), ils doivent être signés avec la même clé.
  2. Les packages de mise à jour OTA doivent être signés avec l'une des clés attendues par le système, sinon le processus d'installation les refusera.

Clés de publication

L'arborescence Android inclut test-keys sous build/target/product/security. La compilation d'une image d'OS Android à l'aide de make signe tous les fichiers .apk à l'aide des clés de test. Étant donné que les clés de test sont connues du public, n'importe qui peut signer ses propres fichiers .apk avec les mêmes clés, ce qui peut lui permettre de remplacer ou de pirater des applications système intégrées à votre image d'OS. C'est pourquoi vous devez impérativement signer une image de l'OS Android publiée ou déployée publiquement, avec un ensemble spécial release-keys auxquelles vous seul avez accès.

Pour générer votre propre ensemble unique de clés de publication, exécutez ces commandes depuis racine de votre arborescence Android:

subject='/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
mkdir ~/.android-certs
for x in releasekey platform shared media networkstack; do \
    ./development/tools/make_key ~/.android-certs/$x "$subject"; \
  done

$subject doit être modifié pour refléter les informations de votre organisation. Vous pouvez utiliser n'importe quel répertoire, mais veillez à choisir un emplacement sauvegardé et sécurisé. Certains fournisseurs choisissent de chiffrer leur clé privée avec une phrase secrète forte et stocker la clé chiffrée dans le contrôle des sources. d'autres stockent leurs clés de libération ailleurs entièrement, comme sur un ordinateur sous air gap.

Pour générer une image de version, utilisez :

make dist
sign_target_files_apks \
-o \    # explained in the next section
--default_key_mappings ~/.android-certs out/dist/*-target_files-*.zip \
signed-target_files.zip

Le script sign_target_files_apks prend un .zip de fichiers cibles en entrée et génère un nouveau .zip de fichiers cibles dans lequel tous les fichiers .apk ont été signés avec de nouvelles clés. Les images nouvellement signées se trouvent sous IMAGES/ dans signed-target_files.zip.

Signer les packages OTA

Un fichier ZIP signé de fichiers cibles peut être converti en fichier ZIP de mise à jour OTA signé. en procédant comme suit:
ota_from_target_files \
-k  (--package_key) 
signed-target_files.zip \
signed-ota_update.zip

Signatures et téléchargement indépendant

Le téléchargement indépendant ne contourne pas la signature normale du package de la récupération. mécanisme de vérification : avant d'installer un package, la récupération vérifie que il est signé avec l'une des clés privées correspondant aux clés publiques stockées dans la partition de récupération, comme elle le ferait pour un paquet livré par voie aérienne.

Les packages de mise à jour reçus du système principal sont généralement vérifiés deux fois : une fois par le système principal, à l'aide de la méthode RecoverySystem.verifyPackage() dans l'API Android, puis à nouveau par la récupération. L'API RecoverySystem vérifie la signature par rapport aux clés publiques stockées dans le système principal, dans le fichier /system/etc/security/otacerts.zip (par défaut). La récupération vérifie la signature par rapport aux clés publiques stockées sur le disque RAM de la partition de récupération, dans le fichier /res/keys.

Par défaut, les fichiers cibles .zip produits par la compilation définissent le Certificat OTA correspondant à la clé de test. Sur une image libérée, une autre certificat doit être utilisé afin que les appareils puissent vérifier l'authenticité "update". Transmettre l'indicateur -o à sign_target_files_apks, comme indiqué dans la section précédente, remplace le certificat de clé de test par le certificat de clé de version de votre répertoire de certificats.

Normalement, l'image système et l'image de récupération stockent le même ensemble d'OTA. clés publiques. En ajoutant une clé à seulement l'ensemble de clés de récupération, vous pouvez signer des packages qui ne peuvent être installés que via le téléchargement parallèle (en supposant que le mécanisme de téléchargement de mise à jour du système principal effectue correctement la vérification par rapport à otacerts.zip). Vous pouvez spécifier des clés supplémentaires à inclure uniquement dans la récupération en définissant la variable PRODUCT_EXTRA_RECOVERY_KEYS dans votre définition de produit :

vendor/yoyodyne/tardis/products/tardis.mk
 [...]

PRODUCT_EXTRA_RECOVERY_KEYS := vendor/yoyodyne/security/tardis/sideload

Cela inclut la clé publique vendor/yoyodyne/security/tardis/sideload.x509.pem dans le fichier de clés de récupération afin qu'elle puisse installer les packages signés avec elle. La clé supplémentaire n'est toutefois pas incluse dans otacerts.zip. Par conséquent, les systèmes qui vérifient correctement les packages téléchargés n'appellent pas de récupération pour les packages signés avec cette clé.

Certificats et clés privées

Chaque clé est fournie dans deux fichiers: le certificat, qui contient le l'extension .x509.pem, et la clé privée, qui porte l'extension .pk8. La clé privée doit être gardée secrète. Elle est nécessaire pour signer un package. La clé peut elle-même être protégée par un mot de passe. Le certificat, dans En revanche, il ne contient que la moitié publique de la clé, qui peut donc être distribué largement. Il permet de vérifier qu'un package a été signé par la personne clé privée.

Le build Android standard utilise cinq clés, qui se trouvent toutes dans build/target/product/security:

testkey
Clé par défaut générique pour les packages qui ne spécifient pas de clé.
plateforme
Clé de test pour les packages qui font partie de la plate-forme de base.
partagé
Clé de test pour les éléments partagés dans le processus de maison/contacts.
Multimédia
Clé de test pour les packages intégrés au système de téléchargement/de médias.
pile réseau
Clé de test pour les packages faisant partie du système de mise en réseau. La clé networkstack est utilisée pour signer les binaires conçus en tant que composants système modulaires. Si les mises à jour de vos modules sont compilées séparément et intégrées en tant que mises à jour prédéfinies dans l'image de votre appareil, vous n'aurez peut-être pas besoin de générer une clé de pile réseau dans Arborescence source Android.

Les packages individuels spécifient l'une de ces clés en définissant LOCAL_CERTIFICATE dans son fichier Android.mk. (testkey est utilisé si cette variable n'est pas définie). Toi peut également spécifier une clé entièrement différente par nom de chemin, par exemple :

device/yoyodyne/apps/SpecialApp/Android.mk
 [...]

LOCAL_CERTIFICATE := device/yoyodyne/security/special

Le build utilise désormais la clé device/yoyodyne/security/special.{x509.pem,pk8} pour signer SpecialApp.apk. La compilation ne peut utiliser que des clés privées ne sont pas protégés par un mot de passe.

Options de signature avancées

Remplacement de la clé de signature de l'APK

Le script de signature sign_target_files_apks fonctionne sur les fichiers cibles générés pour un build. Toutes les informations sur les certificats utilisées au moment de la compilation sont incluses dans les fichiers cibles. Lorsque vous exécutez le script de signature pour signer la version, les clés de signature peuvent être remplacées en fonction du nom de la clé ou du nom de l'APK.

Utiliser --key_mapping et --default_key_mappings pour spécifier le remplacement de clé en fonction des noms de clés:

  • L'option --key_mapping src_key=dest_key spécifie le remplacement d'une seule clé à la fois.
  • L'option --default_key_mappings dir spécifie avec cinq clés pour remplacer toutes les clés de build/target/product/security; cela revient à utiliser --key_mapping cinq fois pour spécifier les mappages.
build/target/product/security/testkey      = dir/releasekey
build/target/product/security/platform     = dir/platform
build/target/product/security/shared       = dir/shared
build/target/product/security/media        = dir/media
build/target/product/security/networkstack = dir/networkstack

Utilisez l'indicateur --extra_apks apk_name1,apk_name2,...=key pour spécifier les remplacements de clés de signature en fonction des noms d'APK. Si key est laissé vide, le script considère les APK spécifiés comme présignés.

Pour le produit tardis fictif, vous avez besoin de six clés protégées par mot de passe: cinq pour remplacer les cinq dans build/target/product/security, et un pour remplacer la clé supplémentaire device/yoyodyne/security/special requises par SpecialApp dans l'exemple ci-dessus. Si les clés se trouvent dans :

vendor/yoyodyne/security/tardis/releasekey.x509.pem
vendor/yoyodyne/security/tardis/releasekey.pk8
vendor/yoyodyne/security/tardis/platform.x509.pem
vendor/yoyodyne/security/tardis/platform.pk8
vendor/yoyodyne/security/tardis/shared.x509.pem
vendor/yoyodyne/security/tardis/shared.pk8
vendor/yoyodyne/security/tardis/media.x509.pem
vendor/yoyodyne/security/tardis/media.pk8
vendor/yoyodyne/security/tardis/networkstack.x509.pem
vendor/yoyodyne/security/tardis/networkstack.pk8
vendor/yoyodyne/security/special.x509.pem
vendor/yoyodyne/security/special.pk8           # NOT password protected
vendor/yoyodyne/security/special-release.x509.pem
vendor/yoyodyne/security/special-release.pk8   # password protected

Vous devez ensuite signer toutes les applications comme suit :

./build/make/tools/releasetools/sign_target_files_apks \
    --default_key_mappings vendor/yoyodyne/security/tardis \
    --key_mapping vendor/yoyodyne/security/special=vendor/yoyodyne/security/special-release \
    --extra_apks PresignedApp= \
    -o tardis-target_files.zip \
    signed-tardis-target_files.zip

Vous obtenez alors les résultats suivants :

Enter password for vendor/yoyodyne/security/special-release key>
Enter password for vendor/yoyodyne/security/tardis/networkstack key>
Enter password for vendor/yoyodyne/security/tardis/media key>
Enter password for vendor/yoyodyne/security/tardis/platform key>
Enter password for vendor/yoyodyne/security/tardis/releasekey key>
Enter password for vendor/yoyodyne/security/tardis/shared key>
    signing: Phone.apk (vendor/yoyodyne/security/tardis/platform)
    signing: Camera.apk (vendor/yoyodyne/security/tardis/media)
    signing: NetworkStack.apk (vendor/yoyodyne/security/tardis/networkstack)
    signing: Special.apk (vendor/yoyodyne/security/special-release)
    signing: Email.apk (vendor/yoyodyne/security/tardis/releasekey)
        [...]
    signing: ContactsProvider.apk (vendor/yoyodyne/security/tardis/shared)
    signing: Launcher.apk (vendor/yoyodyne/security/tardis/shared)
NOT signing: PresignedApp.apk
        (skipped due to special cert string)
rewriting SYSTEM/build.prop:
  replace:  ro.build.description=tardis-user Eclair ERC91 15449 test-keys
     with:  ro.build.description=tardis-user Eclair ERC91 15449 release-keys
  replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys
     with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys
    signing: framework-res.apk (vendor/yoyodyne/security/tardis/platform)
rewriting RECOVERY/RAMDISK/default.prop:
  replace:  ro.build.description=tardis-user Eclair ERC91 15449 test-keys
     with:  ro.build.description=tardis-user Eclair ERC91 15449 release-keys
  replace: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/test-keys
     with: ro.build.fingerprint=generic/tardis/tardis/tardis:Eclair/ERC91/15449:user/release-keys
using:
    vendor/yoyodyne/security/tardis/releasekey.x509.pem
for OTA package verification
done.

Après avoir demandé à l'utilisateur de saisir les mots de passe pour toutes les clés protégées par mot de passe, le script signe à nouveau tous les fichiers APK de la cible d'entrée .zip avec les clés de publication. Avant d'exécuter la commande, vous pouvez également définir la variable d'environnement ANDROID_PW_FILE sur un nom de fichier temporaire. Le script appelle ensuite votre éditeur pour vous permettre de saisir des mots de passe pour toutes les clés (ce qui peut être plus pratique).

Remplacement de la clé de signature APEX

Android 10 introduit le format de fichier APEX pour installer des modules système de niveau inférieur. Comme expliqué dans Signature ApEX, chaque fichier APEX est signé avec deux clés: une pour l'image du mini-système de fichiers dans un élément APEX et l'autre pour pour l'ensemble de l'apex.

Lors de la signature pour la publication, les deux clés de signature d'un fichier APEX sont remplacées avec des clés de déverrouillage. La clé de charge utile du système de fichiers est spécifiée avec le l'option --extra_apex_payload et l'intégralité de la clé de signature du fichier APEX est spécifiée avec l'option --extra_apks.

Pour le produit tardis, supposons que vous disposez de la configuration de clé suivante : pour com.android.conscrypt.apex, com.android.media.apex com.android.runtime.release.apex fichiers APEX.

name="com.android.conscrypt.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED"
name="com.android.media.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED"
name="com.android.runtime.release.apex" public_key="vendor/yoyodyne/security/testkeys/com.android.runtime.avbpubkey" private_key="vendor/yoyodyne/security/testkeys/com.android.runtime.pem" container_certificate="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.x509.pem" container_private_key="vendor/yoyodyne/security/testkeys/com.google.android.runtime.release_container.pk8"

Vous disposez des fichiers suivants contenant les clés de version :

vendor/yoyodyne/security/runtime_apex_container.x509.pem
vendor/yoyodyne/security/runtime_apex_container.pk8
vendor/yoyodyne/security/runtime_apex_payload.pem

La commande suivante remplace les clés de signature pour com.android.runtime.release.apex et com.android.tzdata.apex lors de la signature de la version. En particulier, com.android.runtime.release.apex est signé avec le clés de libération (runtime_apex_container pour le fichier APEX et runtime_apex_payload pour la charge utile de l'image de fichier). com.android.tzdata.apex est traité comme présigné. Tous les autres fichiers APEX sont gérés par la configuration par défaut, comme indiqué dans les fichiers cibles.

./build/make/tools/releasetools/sign_target_files_apks \
    --default_key_mappings   vendor/yoyodyne/security/tardis \
    --extra_apks             com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_container \
    --extra_apex_payload_key com.android.runtime.release.apex=vendor/yoyodyne/security/runtime_apex_payload.pem \
    --extra_apks             com.android.media.apex= \
    --extra_apex_payload_key com.android.media.apex= \
    -o tardis-target_files.zip \
    signed-tardis-target_files.zip

L'exécution de la commande ci-dessus génère les journaux suivants:

        [...]
    signing: com.android.runtime.release.apex                  container (vendor/yoyodyne/security/runtime_apex_container)
           : com.android.runtime.release.apex                  payload   (vendor/yoyodyne/security/runtime_apex_payload.pem)
NOT signing: com.android.conscrypt.apex
        (skipped due to special cert string)
NOT signing: com.android.media.apex
        (skipped due to special cert string)
        [...]

Autres options

Le script de signature sign_target_files_apks réécrit la compilation. une description et une empreinte numérique dans les fichiers de propriétés de compilation pour indiquer que est une compilation signée. L'indicateur --tag_changes contrôle les modifications apportées à l'empreinte digitale. Exécutez le script avec -h pour consulter la documentation sur tous les indicateurs.

Générer manuellement des clés

Android utilise des clés RSA de 2 048 bits avec exposant public 3. Vous pouvez générer des paires de certificat/clé privée à l'aide de l'outil openssl sur openssl.org :

# generate RSA key
openssl genrsa -3 -out temp.pem 2048
Generating RSA private key, 2048 bit long modulus
....+++
.....................+++
e is 3 (0x3)

# create a certificate with the public part of the key
openssl req -new -x509 -key temp.pem -out releasekey.x509.pem -days 10000 -subj '/C=US/ST=California/L=San Narciso/O=Yoyodyne, Inc./OU=Yoyodyne Mobility/CN=Yoyodyne/emailAddress=yoyodyne@example.com'

# create a PKCS#8-formatted version of the private key
openssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt

# securely delete the temp.pem file
shred --remove temp.pem

La commande openssl pkcs8 ci-dessus crée un fichier .pk8 sans mot de passe, adapté à l'utilisation avec le système de compilation. Pour créer un fichier .pk8 sécurisé par un mot de passe (ce que vous devez faire pour toutes les clés de libération), remplacez Argument -nocrypt avec -passout stdin ; puis OpenSSL chiffrera la clé privée avec un mot de passe lu à partir de l’entrée standard. Non l'invite s'affiche. Par conséquent, si le terminal est stdin, le programme semble se figer. quand il n’y a qu’à attendre que vous saisissiez un mot de passe. D'autres valeurs peuvent être utilisées pour l'argument "-passout" afin de lire le mot de passe à partir d'autres emplacements. Pour en savoir plus, consultez la documentation openssl.

Le fichier intermédiaire temp.pem contient la clé privée sans aucune protection par mot de passe. Par conséquent, débarrassez-vous-en de manière réfléchie lorsque vous générez des clés de publication. En particulier, l'utilitaire GNUshred peut ne pas être efficace sur le réseau ou systèmes de fichiers journalisés. Vous pouvez utiliser un répertoire de travail situé sur un disque RAM (comme une partition tmpfs) lors de la génération de clés pour garantir que ne sont pas exposées par inadvertance.

Créer des fichiers image

Une fois que vous disposez de signed-target_files.zip, vous devez créer l'image pour pouvoir la placer sur un appareil. Pour créer l'image signée à partir des fichiers cibles, exécutez la commande suivante à partir de la racine de l'arborescence Android :

img_from_target_files signed-target_files.zip signed-img.zip
Le fichier obtenu, signed-img.zip, contient tous les fichiers .img. Pour charger une image sur un appareil, utilisez fastboot en tant que ce qui suit:
fastboot update signed-img.zip