Pourquoi votre pipeline d'app mobile a besoin de la signature (et comment la sécuriser)
Vous venez de terminer la compilation de votre application Android ou iOS. Le build est vert, les tests passent, et vous êtes prêt à livrer. Mais avant que cet APK ou cet IPA puisse atterrir sur un appareil, il reste une étape qui ressemble souvent à une formalité administrative : la signature de l'application.
Il est tentant de traiter la signature comme une simple case à cocher. Mais si vous avez déjà perdu un keystore, vu un certificat expirer un vendredi soir, ou accidentellement commité un provisioning profile dans un dépôt public, vous savez que c'est là que les choses deviennent sérieuses. La signature n'est pas une simple formalité technique. C'est la couche de sécurité qui prouve que votre application vient bien de vous, et non de quelqu'un qui l'a reconditionnée ou usurpé votre identité.
Ce que fait réellement la signature
Lorsque vous signez une application, vous y attachez une signature numérique qui lie le binaire à votre identité. Cette signature est vérifiée par le système d'exploitation et la boutique d'applications. Si la signature ne correspond pas, l'application ne s'installera pas, ou la boutique rejettera le téléchargement.
Pour Android, la signature utilise un fichier keystore. Ce fichier contient une clé privée et un certificat numérique. Considérez-le comme votre sceau officiel. Chaque fois que vous compilez un APK ou un AAB pour une version de production, vous apposez ce sceau avec ce keystore. Si vous utilisez un keystore différent plus tard, Android traite l'application comme une application complètement différente, même si le nom du package est identique. Cela signifie que les utilisateurs ne peuvent pas mettre à jour l'application par-dessus l'installation existante. Ils devraient d'abord désinstaller l'ancienne version, perdant ainsi toutes les données locales.
Pour iOS, le processus est plus complexe. Vous avez besoin de deux choses : un certificat et un provisioning profile. Le certificat est votre identité numérique en tant que développeur. Le provisioning profile lie le certificat, l'App ID et la liste des appareils autorisés à exécuter l'application. Pour la distribution sur l'App Store, vous utilisez un certificat de distribution et un provisioning profile App Store. Pour les tests internes ou le développement, vous utilisez un certificat de développement et un provisioning profile ad-hoc.
Le vrai problème : garder les secrets dans un pipeline
Une fois que vous comprenez ce que la signature implique, la question suivante est évidente : comment stocker ces identifiants dans votre pipeline CI/CD sans les écrire dans votre code ou vos fichiers de configuration ?
La réponse est la gestion des secrets. Mais soyons clairs sur ce qu'il ne faut surtout pas faire.
Ne stockez jamais les keystores, certificats ou provisioning profiles dans votre dépôt Git. Ces fichiers sont des secrets, pas de la configuration. S'ils se retrouvent dans un dépôt public, n'importe qui peut signer des applications en se faisant passer pour vous. S'ils se retrouvent dans un dépôt privé, vous avez toujours un problème : chaque développeur ayant accès au dépôt détient désormais vos clés de signature de production. C'est un risque de sécurité et d'audit.
Utilisez plutôt le coffre de secrets fourni par votre plateforme CI/CD. GitHub Actions, GitLab CI, Jenkins et la plupart des autres plateformes disposent de variables secrètes intégrées. Vous pouvez télécharger votre keystore ou certificat sous forme de chaîne encodée en base64, la stocker comme variable secrète, et la décoder en fichier pendant l'exécution du pipeline. Le secret n'est jamais écrit sur le disque de la machine de build jusqu'à l'exécution, et il n'apparaît jamais dans les logs.
Voici un exemple concret pour Android avec GitHub Actions :
- name: Decode keystore
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > app/release.keystore
Pour iOS avec Fastlane et GitLab CI :
- name: Decode certificate
run: echo $MATCH_PASSWORD | fastlane match import --git_url $MATCH_REPO
Si votre équipe a besoin de plus de contrôle, envisagez d'utiliser un gestionnaire de secrets dédié comme AWS Secrets Manager, Azure Key Vault ou HashiCorp Vault. Votre pipeline récupère les identifiants à l'exécution depuis ces services. Cette approche vous offre des journaux d'audit, un contrôle d'accès et une rotation centralisée. Les identifiants ne résident jamais dans la configuration du pipeline elle-même.
Voici un script bash complet qui récupère le keystore depuis AWS Secrets Manager, signe l'APK avec jarsigner et vérifie la signature :
#!/bin/bash
set -euo pipefail
# Fetch keystore from AWS Secrets Manager
KEYSTORE_SECRET=$(aws secretsmanager get-secret-value \
--secret-id "mobile-app/keystore" \
--query SecretString --output text)
echo "$KEYSTORE_SECRET" | base64 --decode > /tmp/release.keystore
# Sign the APK
jarsigner -verbose -sigalg SHA256withRSA \
-digestalg SHA-256 \
-keystore /tmp/release.keystore \
-storepass "$STORE_PASSWORD" \
app-release-unsigned.apk \
mykeyalias
# Verify the signature
jarsigner -verify -verbose -certs app-release-unsigned.apk
# Clean up
rm -f /tmp/release.keystore
Ce script garantit que le keystore n'est jamais stocké dans le dépôt, qu'il est récupéré de manière sécurisée à l'exécution et qu'il est nettoyé immédiatement après la signature.
Expiration des certificats : le tueur silencieux du pipeline
Voici un scénario qui arrive plus souvent qu'il ne le devrait. Votre pipeline fonctionne parfaitement pendant des mois. Puis un jour, un build de release échoue. Vous fouillez les logs et découvrez que le certificat de signature a expiré. L'application déjà présente dans la boutique fonctionne toujours correctement sur les appareils des utilisateurs. Mais vous ne pouvez pas télécharger une nouvelle version. La boutique la rejette car la signature du nouveau binaire est invalide.
Les keystores Android et les certificats iOS ont des dates d'expiration. Ils ne se renouvellent pas automatiquement. Votre pipeline devrait détecter les expirations à venir et alerter l'équipe avant que l'identifiant ne devienne inutilisable. Un simple script qui vérifie la date de validité du keystore ou du certificat et envoie une notification sur le canal de discussion de votre équipe peut vous éviter un blocage de release.
Pour Android, vous pouvez vérifier l'expiration du keystore avec :
keytool -list -v -keystore release.keystore -storepass $STORE_PASSWORD | grep "Valid until"
Pour iOS, l'outil match de Fastlane inclut un mode --readonly qui affiche l'expiration du certificat. Vous pouvez également utiliser la commande security sur macOS :
security find-identity -v -p codesigning | grep "iPhone Distribution"
Une checklist pratique pour la signature dans votre pipeline
Si vous configurez la signature pour la première fois ou si vous révisez votre configuration actuelle, parcourez cette checklist :
- Les keystores, certificats et provisioning profiles sont-ils stockés en dehors de Git ?
- Les identifiants de signature sont-ils stockés dans le coffre de secrets de la plateforme CI/CD ou dans un gestionnaire de secrets externe ?
- Le keystore ou certificat encodé en base64 est-il stocké comme variable secrète, et non dans un fichier de configuration ?
- Le pipeline décode-t-il le secret uniquement à l'exécution, dans un répertoire temporaire ?
- Les fichiers de signature temporaires sont-ils nettoyés après la fin du build ?
- Le pipeline vérifie-t-il l'expiration du certificat et alerte-t-il l'équipe avant que l'identifiant n'expire ?
- Existe-t-il un processus documenté pour la rotation ou le renouvellement des identifiants de signature ?
- Les identifiants de signature de production sont-ils restreints à un petit ensemble de membres de confiance de l'équipe ?
La signature n'est pas la fin
Une fois votre application signée, l'artefact est prêt pour les tests. Mais un binaire signé posé sur une machine de build n'est pas la même chose qu'une release testée. Les applications mobiles ne peuvent pas être entièrement vérifiées en lisant le code ou en exécutant uniquement des tests unitaires. Vous devez exécuter l'application signée sur des émulateurs, des simulateurs ou des appareils réels pour détecter les problèmes qui n'apparaissent qu'à l'exécution.
L'étape de signature est une porte de contrôle. Elle garantit que ce que vous allez tester et livrer est authentiquement le vôtre. Traitez-la avec le même soin que vous accordez à vos identifiants de base de données de production. Car à bien des égards, elle est plus précieuse : perdre un mot de passe de base de données signifie restaurer à partir d'une sauvegarde. Perdre votre keystore signifie perdre la capacité de mettre à jour votre application entièrement.