# ITMS-90034 : corriger « Missing or invalid signature »

> ITMS-90034 : ton build n'est pas signé avec un certificat de distribution valide. Lis sa signature avec codesign, puis refais un build store avec EAS.

Page originale : [ITMS-90034 : corriger « Missing or invalid signature »](https://devoria.fr/blog/itms-90034-signature-invalide)
Auteur : [Mehdi Azizi](https://devoria.fr/mehdi-azizi)
Publication : 2026-11-04
Dernière révision du contenu : 2026-11-04


ITMS-90034 arrive quand tu envoies ton build à App Store Connect. Il dit que ton app, ou l'un de ses composants, n'est pas signée avec un certificat de soumission Apple, c'est-à-dire un certificat de distribution valide. **La correction : trouver quel bundle est mal signé et pourquoi, puis refaire un build signé pour l'App Store.**

Ce guide reste centré sur l'erreur. Si les mots certificat et profil te sont flous, lis d'abord [certificat développeur iOS et provisioning profile](https://devoria.fr/blog/certificat-provisioning-profile-ios). Ici, tu trouveras le message à lire, un arbre de décision, les commandes qui inspectent ton IPA et la correction avec les identifiants gérés par EAS. Pour la partie EAS, j'ai lu le code d'EAS CLI 24.10.0 qui choisit le certificat.

## Que veut dire l'erreur ITMS-90034 ?

Voici sa forme classique, avec des noms d'exemple :

> ITMS-90034: Missing or invalid signature. The bundle 'com.exemple.monapp' at bundle path 'Payload/MonApp.app' is not signed using an Apple submission certificate.

En 2026, sur le forum d'Apple, elle apparaît aussi sous la forme « Validation failed (409) Missing or invalid signature », suivie du même texte et de « (ID: 90034) ».

Le « certificat de soumission », c'est le certificat de distribution. [L'aide d'Apple sur les certificats](https://developer.apple.com/help/account/certificates/certificates-overview) l'écrit : les certificats de développement servent à lancer ton app sur tes appareils, ceux de distribution à la distribuer ou à l'envoyer à App Store Connect.

**Lis le chemin cité : il désigne le bundle fautif.** `Payload/MonApp.app` vise l'app elle-même. Un chemin qui finit par `.appex` vise une extension, comme un widget. Un chemin en `.framework` vise une bibliothèque embarquée.

Ce n'est pas le refus d'un reviewer. La validation tombe à l'envoi ou pendant le traitement du build, avant TestFlight : ce build n'atteindra jamais tes testeurs.

## L'arbre de décision : du message à la correction

Chaque ligne correspond à un indice que tu lis dans le message, dans les commandes de la section suivante ou dans les journaux d'EAS. Prends-les dans l'ordre : la première qui correspond donne ta piste.

| Ce que tu observes        | Cause probable          | Correction              |
| ------------------------- | ----------------------- | ----------------------- |
| Chemin en `.appex`        | Extension mal signée    | Déclarer l'extension    |
| `Signature=adhoc`         | Build non signé         | Build profil store      |
| `Apple Development`       | Build de développement  | Build profil store      |
| `ProvisionedDevices` lu   | Build interne (ad hoc)  | Build profil store      |
| `sealed resource`         | IPA modifiée après coup | Refaire le build        |
| EAS : « no longer valid » | Certificat révoqué      | Nouveau certificat      |
| Tout est valide           | Incident côté Apple     | Renvoyer, puis signaler |

## Inspecter ton IPA en quatre commandes

Télécharge l'IPA depuis la page du build sur expo.dev, puis ouvre un terminal sur ton Mac.

**1. Qui a signé l'app ?**

```bash
unzip -q MonApp.ipa -d ipa
codesign -dv --verbose=4 ipa/Payload/*.app 2>&1 | grep -E "^(Identifier|Authority|Signature|TeamIdentifier)="
```

`codesign` écrit sur la sortie d'erreur, d'où le `2>&1`. Selon [la note technique TN3161 d'Apple](https://developer.apple.com/documentation/technotes/tn3161-inside-code-signing-certificates), chaque ligne `Authority` est un maillon de la chaîne de confiance. La première doit commencer par `Apple Distribution:` ou `iPhone Distribution:`, suivie du nom de ton équipe. Le second libellé est celui de l'ancien type iOS Distribution : d'après son code, EAS CLI 24.10.0 crée encore des certificats de ce type. Si tu lis `Apple Development:`, c'est un build de développement. Si tu lis `Signature=adhoc` sans aucune ligne `Authority`, le binaire n'est signé par aucun certificat.

**2. L'app a-t-elle été modifiée après la signature ?**

```bash
codesign --verify --deep --strict -vv ipa/Payload/*.app
```

Tu veux lire `valid on disk`, puis `satisfies its Designated Requirement`. TN3161 précise ce que ce contrôle garantit : tous les fichiers attendus sont là, aucun n'a été ajouté, aucun n'a été modifié. Sur un bundle de test que j'ai signé puis retouché, la commande répond `a sealed resource is missing or invalid`, puis `file modified:` suivi du fichier changé. L'option `--deep` sert ici à vérifier, jamais à signer : sur le forum d'Apple, un ingénieur de l'équipe support le déconseille pour signer.

**3. Quel profil est embarqué ?**

```bash
security cms -D -i ipa/Payload/*.app/embedded.mobileprovision -o profil.plist
plutil -extract ProvisionedDevices xml1 -o - profil.plist
plutil -extract ExpirationDate raw profil.plist
```

Ce sont les commandes de [la note TN3125 sur les profils](https://developer.apple.com/documentation/technotes/tn3125-inside-code-signing-provisioning-profiles). Un profil App Store ne contient aucune liste d'appareils : la deuxième ligne doit répondre `Could not extract value`. Si elle affiche des identifiants d'appareils, c'est un profil ad hoc, celui d'un build interne. La troisième donne la date d'expiration du profil.

**4. Les extensions et les frameworks sont-ils signés pareil ?**

```bash
find ipa/Payload/*.app \( -name "*.appex" -o -name "*.framework" \) \
  -exec sh -c 'echo "$1"; codesign -dvv "$1" 2>&1 | grep -m1 "^Authority="' _ {} \;
```

Chaque composant doit afficher la même ligne `Authority` que l'app. Si l'un d'eux s'en écarte, c'est lui que cite le message.

Si tu veux vérifier avant l'envoi, TN3161 recommande `altool`, le même contrôle que le bouton Validate App de Xcode. Il demande une clé API App Store Connect, dont le fichier `AuthKey_<ID_CLE>.p8` se range dans `~/.appstoreconnect/private_keys` :

```bash
xcrun altool --validate-app -f MonApp.ipa -t ios --api-key <ID_CLE> --api-issuer <ID_EMETTEUR>
```

## Corriger avec EAS, cause par cause

### Un build interne ou de développement envoyé à Apple

D'après la [documentation d'Expo sur eas.json](https://docs.expo.dev/build/eas-json/), les builds en `"distribution": "internal"` ne sont pas signés pour les stores : ils utilisent un profil ad hoc. Seul un profil en distribution `store`, la valeur par défaut, produit un build envoyable. **Le plus sûr est d'envoyer le build que tu viens de construire, sans le choisir à la main** :

```bash
eas build --platform ios --profile production --auto-submit
```

Si tu passes par `eas submit --path`, vérifie que le fichier est bien ton dernier build store. Sur le forum d'Apple, une équipe a découvert que son outil d'envoi renvoyait à chaque fois un ancien IPA, versionné dans git.

### Une extension oubliée

Chaque extension est un bundle à part, avec son identifiant et son propre profil. Dans un projet Expo à génération native, la [page d'Expo sur les extensions iOS](https://docs.expo.dev/build-reference/app-extensions/) demande de les déclarer dans `extra.eas.build.experimental.ios.appExtensions`. EAS CLI les connaît alors avant le build, et crée ou valide leurs identifiants. La plupart des bibliothèques qui ajoutent une extension écrivent cette déclaration pour toi, via leur plugin.

Solys, mon réveil à mini-jeux, embarque quatre extensions : un widget et trois extensions de Temps d'écran. Les quatre sont déclarées dans sa configuration, et EAS a un profil pour chacune. **Une extension ajoutée à la main dans Xcode, sans cette déclaration, est le candidat idéal** pour un chemin en `.appex` dans le message.

### Un certificat révoqué ou expiré

La [page d'Apple sur les certificats](https://developer.apple.com/support/certificates/) est claire : tu ne peux plus envoyer de build signé avec un certificat expiré ou révoqué. Un build déjà envoyé mais pas encore soumis peut même passer en Invalid Binary si son certificat a été révoqué.

Le code d'EAS CLI 24.10.0 montre quand ce cas passe inaperçu :

- **Build interactif, connecté à Apple** : EAS compare son certificat avec ceux encore valides chez Apple. S'il n'y est plus, il affiche « Current distribution certificate is no longer valid in Apple Dev Portal » et te propose d'en choisir ou d'en créer un.
- **Build interactif, sans connexion** : EAS ne vérifie que les dates et prévient qu'il saute la validation chez Apple.
- **Build non interactif, en intégration continue** : EAS affiche « Distribution Certificate is not validated for non-interactive builds » et signe quand même.

**En intégration continue, EAS ne revérifie pas le certificat lui-même : une révocation peut ne se voir qu'à l'envoi.** La correction : lance un build interactif connecté à ton compte Apple, ou passe par `eas credentials --platform ios`, menu Build Credentials, puis « Distribution Certificate: Add a new one to your account ». Les profils qui contenaient l'ancien certificat deviennent invalides : EAS les contrôle au build suivant et les refait. Les limites d'Apple sur le nombre de certificats sont détaillées dans le [guide sur le certificat développeur iOS](https://devoria.fr/blog/certificat-provisioning-profile-ios).

### Un binaire modifié après la signature

La signature scelle le code, les ressources et les métadonnées par des empreintes, comme l'explique [la note TN3126 d'Apple](https://developer.apple.com/documentation/technotes/tn3126-inside-code-signing-hashes). Décompresser l'IPA pour changer un fichier, puis la recompresser, casse ce sceau. **Fais la modification dans ta configuration, puis refais un build.** Ne retouche jamais l'IPA produite par EAS.

Même logique pour du code rangé au mauvais endroit, par exemple dans une archive `.zip` : l'App Store ne peut pas le re-signer. Apple décrit les emplacements autorisés dans sa page sur [le contenu d'un bundle](https://developer.apple.com/documentation/bundleresources/placing-content-in-a-bundle).

### Des identifiants locaux

Si ton profil EAS utilise `"credentialsSource": "local"`, ton fichier `credentials.json` pointe vers un certificat `.p12` et un profil `.mobileprovision`. Vérifie le profil avec les commandes de l'étape 3 : il doit être de type App Store et contenir ton certificat de distribution actuel. Et garde ces fichiers hors du dépôt, comme [les clés API d'une app Expo](https://devoria.fr/blog/cle-api-secrete-app-expo).

### Quand tout est valide

Il reste un cas frustrant. Sur le forum d'Apple, des développeurs voient l'erreur disparaître en renvoyant le même build, sans rien changer. En août et septembre 2026, deux fils décrivent l'inverse : certificat et profil neufs, signature vérifiée, échec persistant. Dans l'un d'eux, un ingénieur d'Apple conseille un rapport de bug avec l'IPA, l'analyse de signature et les journaux de Transporter, puis un contact avec App Store Connect.

Renvoie donc une fois avec un nouveau numéro de build. Si l'erreur revient alors que tes quatre commandes sont propres, signale-la à Apple avec ces éléments. **Ne révoque pas ton certificat par réflexe** : ça ne résout rien de plus et ça invalide tes profils.

## Envoyer ton build sans mauvaise surprise

Une signature se vérifie en deux minutes avant l'envoi. Les autres contrôles automatiques demandent la même rigueur, comme [ITMS-90725](https://devoria.fr/blog/itms-90725-sdk-version-xcode) pour la version de Xcode ou [ITMS-90683](https://devoria.fr/blog/itms-90683-missing-purpose-string-expo) pour les phrases d'autorisation. Le parcours complet du build à l'envoi, sans Mac, est dans [publier une app iOS sans Mac](https://devoria.fr/blog/publier-une-app-ios-sans-mac).

Les refus d'un reviewer demandent plus de recul : la démarche est sur la page [application refusée par Apple](https://devoria.fr/expertise/application-refusee-par-apple), et les motifs les plus fréquents sont dans [pourquoi Apple refuse une app](https://devoria.fr/blog/pourquoi-apple-refuse-votre-app).

Pour vérifier ton dossier avant l'envoi, le [Kit Review Apple](https://devoria.fr/kits/review-apple) réunit ma checklist et des skills Claude Code qui relisent ton projet. Si ton envoi bloque et que tu veux un regard humain, [réserve une heure avec moi](https://devoria.fr/coaching) : on reprend ton projet en visio, sur ton écran. Et si tu prépares ta première sortie, [fais ton diagnostic gratuit](https://devoria.fr/diagnostic) : il remet les étapes dans l'ordre et t'envoie ma checklist anti-refus Apple.

## Questions fréquentes

### ITMS-90034 est-il un refus d'App Review ?

Non. C'est une validation automatique faite à l'envoi du build, avant TestFlight et avant toute revue. Personne n'a ouvert ton app : Apple a seulement constaté que sa signature ne vient pas d'un certificat de distribution valide.

### Faut-il révoquer mon certificat de distribution pour corriger ITMS-90034 ?

Seulement s'il est compromis ou déjà invalide. Révoquer un certificat qui fonctionne invalide tes profils sans corriger la cause. Commence par lire la signature de ton IPA avec `codesign`, puis corrige ce que l'arbre de décision désigne.

### Peut-on corriger ITMS-90034 avec une mise à jour à distance ?

Non. La signature fait partie du binaire, et le build refusé ne sera jamais utilisable. Il faut produire un nouveau binaire correctement signé et le renvoyer.

### Pourquoi l'erreur cite-t-elle mon widget et pas mon app ?

Parce que le widget est un bundle à part, signé avec son propre profil. S'il n'est pas déclaré à EAS ou qu'il a été signé autrement, il fait échouer tout l'envoi. Déclare l'extension dans ta configuration et refais un build.

### Que faire si codesign dit que tout est valide ?

Vérifie aussi le profil embarqué et chaque extension. Si tout est propre, renvoie une fois avec un nouveau numéro de build. Si l'erreur persiste, envoie un rapport à Apple avec l'IPA, la sortie de `codesign` et les journaux d'envoi.


## Ton projet

[Kit Review](https://devoria.fr/kits/review-apple) : Tout ce que je vérifie avant d'envoyer une app à Apple, la même vérification pour Google Play, et trois skills qui font la relecture avec Claude Code. 49 €, paiement unique. Inclus : La checklist avant l'envoi ; Les refus que j'ai pris, et ce que j'ai corrigé ; Les notes pour le testeur ; Les skills de vérification. Prérequis : une app iPhone ou Android presque prête, et Claude Code ou Cursor. Téléchargement immédiat après le paiement.

[Kit iOS & Android Devoria](https://devoria.fr/kits/ios) : Le code de départ de mes apps, le parcours jusqu'à l'App Store et Google Play avec les messages à donner à Claude, et tous les kits. Claude part d'une app qui tourne déjà. 99 €, paiement unique. Inclus : Le code de départ de ton app ; Les styles visuels ; Le parcours pas à pas ; Kit Review ; Kit Captures ; Kit ASO ; Mises à jour à vie ; Bonus : la séance 1:1 à moitié prix. Prérequis : pour l'iPhone, un Mac avec Xcode 26 ou plus et un compte Apple Developer (99 $ par an) pour publier. Pour Android, Android Studio sur un Mac, un PC Windows ou Linux, et un compte Google Play Console (25 $, une fois). Et Claude Code (abonnement payant) ou Cursor. Téléchargement immédiat après le paiement.

[Diagnostic gratuit](https://devoria.fr/diagnostic) : quelques questions sur ton projet. Il dit ce qui te bloque vraiment, l'ordre des quatre étapes et le kit qui correspond. En laissant ton e-mail, tu reçois la checklist anti-refus Apple : les points vérifiés avant chaque envoi, tirés des refus reçus par les apps du studio.

[Une heure avec moi](https://devoria.fr/coaching) : On regarde ton app ou ton projet Claude, écran partagé : ce qui bloque, ce que je change en premier, et dans quel ordre. 100 € la séance d'une heure, en visio, paiement en une fois. Sujets : L'audit de ton app ; Ton travail avec Claude ; Ta fiche et ton paywall ; Un refus d'Apple.
