Les images du système d'exploitation Android utilisent des signatures cryptographiques à deux endroits :
- Chaque fichier
.apkde l'image doit être signé. Le gestionnaire de packages d'Android utilise une signature.apkde deux manières :- Lorsqu'une application est remplacée, elle doit être signée avec la même clé que l'ancienne application pour pouvoir accéder à ses données. Cela vaut aussi bien pour la mise à jour des applications utilisateur en écrasant le fichier
.apkque pour le remplacement d'une application système par une version plus récente installée sous/data. - Si deux applications ou plus souhaitent partager un ID utilisateur (afin de pouvoir partager des données, etc.), elles doivent être signées avec la même clé.
- Lorsqu'une application est remplacée, elle doit être signée avec la même clé que l'ancienne application pour pouvoir accéder à ses données. Cela vaut aussi bien pour la mise à jour des applications utilisateur en écrasant le fichier
- 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 rejettera.
Clés de version
L'arborescence Android inclut des clés de test sous build/target/product/security. La création d'une image du système d'exploitation Android à l'aide de make signera tous les fichiers .apk à l'aide des clés de test. Étant donné que les clés de test sont publiques, n'importe qui peut signer ses propres fichiers .apk avec les mêmes clés, ce qui peut lui permettre de remplacer ou de détourner des applications système intégrées à votre image du système d'exploitation. C'est pourquoi il est essentiel de signer toute image du système d'exploitation Android publiée ou déployée publiquement avec un ensemble spécial de clés de version auxquelles vous seul avez accès.
Pour générer votre propre ensemble unique de clés de version, exécutez les commandes suivantes à partir de la 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-certsfor 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 complexe et de stocker la clé chiffrée dans le contrôle de code source. D'autres stockent leurs clés de version ailleurs, par exemple sur un ordinateur isolé.
Pour générer une image de version, utilisez :
make distsign_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 fichier .zip de fichiers cibles en entrée et produit un nouveau fichier .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 des packages OTA
Un fichier zip de fichiers cibles signé peut être converti en fichier zip de mise à jour OTA signé à l'aide de la procédure suivante :
ota_from_target_files \
-k (--package_key)
signed-target_files.zip \
signed-ota_update.zip
Signatures et chargement latéral
Le chargement latéral ne contourne pas le mécanisme normal de vérification de la signature du package de récupération. Avant d'installer un package, la récupération vérifie qu'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 package fourni par voie hertzienne.
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 une autre fois 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 dans le disque RAM de la partition de récupération, dans le fichier /res/keys.
Par défaut, le fichier .zip de fichiers cibles produit par la compilation définit le certificat OTA pour qu'il corresponde à la clé de test. Sur une image publiée, un autre certificat doit être utilisé pour que les appareils puissent vérifier l'authenticité du package de mise à jour. Le passage de 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 de clés publiques OTA. En ajoutant une clé uniquement à l'ensemble de clés de récupération, il est possible de signer des packages qui ne peuvent être installés que par chargement latéral (en supposant que le mécanisme de téléchargement de la 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 la définition de votre 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 pas incluse dans otacerts.zip. Par conséquent, les systèmes qui vérifient correctement les packages téléchargés n'appellent pas la 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 porte l' extension .x509.pem, et la clé privée, qui porte l'extension .pk8. La clé privée doit rester secrète et est nécessaire pour signer un package. La clé elle-même peut être protégée par un mot de passe. Le certificat, en revanche, ne contient que la moitié publique de la clé. Il peut donc être distribué à grande échelle. Il permet de vérifier qu'un package a été signé par la clé privée correspondante.
La compilation Android standard utilise cinq clés, qui résident toutes dans
build/target/product/security :
- testkey
- Clé par défaut générique pour les packages qui ne spécifient pas de clé.
- platform
- Clé de test pour les packages qui font partie de la plate-forme de base.
- shared
- Clé de test pour les éléments partagés dans le processus de maison/contacts.
- media
- Clé de test pour les packages qui font partie du système multimédia/de téléchargement.
Les packages individuels spécifient l'une de ces clés en définissant LOCAL_CERTIFICATE dans leur
Android.mk fichier. (testkey est utilisé si cette variable n'est pas définie.) Vous pouvez également spécifier
une clé entièrement différente par chemin d'accès, par exemple :
device/yoyodyne/apps/SpecialApp/Android.mk
[...] LOCAL_CERTIFICATE := device/yoyodyne/security/special
La compilation utilise désormais la clé device/yoyodyne/security/special.{x509.pem,pk8}
pour signer SpecialApp.apk. La compilation ne peut utiliser que les clés privées qui
ne sont pas protégées 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 une compilation. Toutes les informations sur les certificats et les clés privées utilisés lors de la compilation sont incluses dans les fichiers cibles. Lorsque vous exécutez le script de signature pour signer une version, les clés de signature peuvent être remplacées en fonction du nom de la clé ou du nom de l'APK.
Utilisez les indicateurs --key_mapping et --default_key_mappings pour spécifier le remplacement de la clé en fonction des noms de clé :
- L'indicateur
--key_mapping src_key=dest_keyspécifie le remplacement d'une clé à la fois. - L'indicateur
--default_key_mappings dirspécifie un répertoire contenant cinq clés pour remplacer toutes les clés dansbuild/target/product/security. Il équivaut à utiliser--key_mappingcinq 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'
--extra_apks apk_name1,apk_name2,...=key indicateur
pour spécifier les remplacements de clés de signature en fonction des noms d'APK. Si key est laissé vide, le script traite les APK spécifiés comme pré-signés.
Pour le produit tardis hypothétique, vous avez besoin de six clés protégées par mot de passe : cinq pour remplacer les cinq clés dans build/target/product/security et une pour remplacer la clé supplémentaire device/yoyodyne/security/special requise par SpecialApp dans l'exemple ci-dessus. Si les clés se trouvaient dans les fichiers suivants :
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 signeriez ensuite 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
Le résultat est le suivant :
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 invité l'utilisateur à saisir les mots de passe de toutes les clés protégées par mot de passe, le script signe de nouveau tous les fichiers APK du fichier .zip cible d'entrée avec les clés de version. 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 les mots de passe de toutes les clés (il s'agit peut-être d'un moyen plus pratique de saisir les mots de passe).
Remplacement de la clé de signature APEX
Android 10 introduit le format de fichier APEX pour l'installation de modules système de niveau inférieur. Comme expliqué dans la section Signature APEX, chaque fichier APEX est signé avec deux clés : une pour l'image du mini-système de fichiers dans un APEX et l' autre pour l'ensemble de l'APEX.
Lors de la signature d'une version, les deux clés de signature d'un fichier APEX sont remplacées par des clés de version. La clé de charge utile du système de fichiers est spécifiée avec l'indicateur --extra_apex_payload, et la clé de signature de l'ensemble du fichier APEX est spécifiée avec l'indicateur --extra_apks.
Pour le produit tardis, supposons que vous disposez de la configuration de clé suivante pour les fichiers APEX com.android.conscrypt.apex, com.android.media.apex et com.android.runtime.release.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"
Et 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 de 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 les clés de version spécifiées (runtime_apex_container pour le fichier APEX et runtime_apex_payload pour la charge utile de l'image du 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 description de la compilation et l'empreinte dans les fichiers de propriétés de la compilation pour indiquer qu'il s'agit d'une compilation signée. L'indicateur --tag_changes contrôle les modifications apportées à l'empreinte. Exécutez le script avec -h pour afficher la documentation sur tous les indicateurs.
Générer des clés manuellement
Android utilise des clés RSA de 2 048 bits avec un exposant public de 3. Vous pouvez générer des paires de clés privées/de certificats à l'aide de l'outil openssl depuis openssl.org :
# generate RSA keyopenssl genrsa -3 -out temp.pem 2048Generating RSA private key, 2048 bit long modulus ....+++ .....................+++ e is 3 (0x3) # create a certificate with the public part of the keyopenssl 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 keyopenssl pkcs8 -in temp.pem -topk8 -outform DER -out releasekey.pk8 -nocrypt# securely delete the temp.pem fileshred --remove temp.pem
La commande openssl pkcs8 ci-dessus crée un fichier .pk8 sans mot de passe, adapté à une utilisation avec le système de compilation. Pour créer un fichier .pk8 sécurisé avec un mot de passe (ce que vous devez faire pour toutes les clés de version réelles), remplacez l'argument -nocrypt par -passout stdin. openssl chiffrera ensuite la clé privée avec un mot de passe lu à partir de l'entrée standard. Aucune invite n'est affichée. Par conséquent, si l'entrée standard est le terminal, le programme semble se bloquer alors qu'il attend simplement 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 aucun type de protection par mot de passe. Veillez donc à vous en débarrasser de manière réfléchie lorsque vous générez des clés de version. En particulier, l'utilitaire GNUshred peut ne pas être efficace sur les systèmes de fichiers réseau ou journalisés. Vous pouvez utiliser un répertoire de travail situé sur un disque RAM (tel qu'une partition tmpfs) lorsque vous générez des clés pour vous assurer que les intermédiaires ne sont pas exposés 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
signed-img.zip, contient tous les fichiers .img.
Pour charger une image sur un appareil, utilisez fastboot comme suit :
fastboot update signed-img.zip