ITMS-90034: rezolvă „Missing or invalid signature”
De Mehdi Azizi
Publicat pe , 8 min.
ITMS-90034: build-ul tău nu e semnat cu un certificat de distribuție valid. Citește-i semnătura cu codesign, apoi refă un build store cu EAS.
ITMS-90034 apare când trimiți build-ul la App Store Connect. Îți spune că aplicația ta, sau una dintre componentele ei, nu e semnată cu un certificat de trimitere Apple, adică un certificat de distribuție valid. Soluția: găsești ce bundle e semnat greșit și de ce, apoi refaci un build semnat pentru App Store.
Ghidul ăsta rămâne concentrat pe eroare. Dacă termenii certificat și profil nu-ți sunt clari, citește mai întâi certificatul de dezvoltator iOS și provisioning profile. Aici găsești mesajul de citit, un arbore de decizie, comenzile care îți inspectează IPA-ul și rezolvarea cu credențialele gestionate de EAS. Pentru partea de EAS, am citit codul din EAS CLI 24.10.0 care alege certificatul.
Ce înseamnă eroarea ITMS-90034?
Iată forma ei clasică, cu nume de exemplu:
ITMS-90034: Missing or invalid signature. The bundle 'com.example.myapp' at bundle path 'Payload/MyApp.app' is not signed using an Apple submission certificate.
În 2026, pe forumul Apple, apare și sub forma „Validation failed (409) Missing or invalid signature”, urmată de același text și de „(ID: 90034)”.
„Certificatul de trimitere” e certificatul de distribuție. Ajutorul Apple despre certificate o spune clar: certificatele de dezvoltare servesc la rularea aplicației pe dispozitivele tale, cele de distribuție la distribuirea ei sau la trimiterea ei la App Store Connect.
Citește calea menționată: ea arată bundle-ul vinovat. Payload/MyApp.app se referă la aplicația propriu-zisă. O cale care se termină în .appex se referă la o extensie, cum ar fi un widget. O cale în .framework se referă la o bibliotecă inclusă.
Nu e o respingere de la un reviewer. Validarea pică la trimitere sau în timpul procesării build-ului, înainte de TestFlight: build-ul ăsta nu va ajunge niciodată la testerii tăi.
Arborele de decizie: de la mesaj la rezolvare
Fiecare rând corespunde unui indiciu pe care îl citești în mesaj, în comenzile din secțiunea următoare sau în logurile EAS. Ia-le în ordine: primul care se potrivește îți dă pista.
| Ce observi | Cauză probabilă | Rezolvare |
|---|---|---|
Cale în .appex | Extensie semnată greșit | Declară extensia |
Signature=adhoc | Build nesemnat | Build cu profil store |
Apple Development | Build de dezvoltare | Build cu profil store |
ProvisionedDevices citit | Build intern (ad hoc) | Build cu profil store |
sealed resource | IPA modificat ulterior | Refă build-ul |
| EAS: „no longer valid” | Certificat revocat | Certificat nou |
| Totul e valid | Incident la Apple | Retrimite, apoi raportează |
Inspectează-ți IPA-ul în patru comenzi
Descarcă IPA-ul de pe pagina build-ului de pe expo.dev, apoi deschide un terminal pe Mac.
1. Cine a semnat aplicația?
unzip -q MyApp.ipa -d ipa
codesign -dv --verbose=4 ipa/Payload/*.app 2>&1 | grep -E "^(Identifier|Authority|Signature|TeamIdentifier)="
codesign scrie pe ieșirea de eroare, de aici 2>&1. Conform notei tehnice TN3161 de la Apple, fiecare linie Authority e o verigă din lanțul de încredere. Prima trebuie să înceapă cu Apple Distribution: sau iPhone Distribution:, urmată de numele echipei tale. A doua etichetă e cea a vechiului tip iOS Distribution: după codul său, EAS CLI 24.10.0 încă mai creează certificate de tipul ăsta. Dacă vezi Apple Development:, e un build de dezvoltare. Dacă vezi Signature=adhoc fără nicio linie Authority, binarul nu e semnat de niciun certificat.
2. A fost modificată aplicația după semnare?
codesign --verify --deep --strict -vv ipa/Payload/*.app
Vrei să vezi valid on disk, apoi satisfies its Designated Requirement. TN3161 precizează ce garantează verificarea asta: toate fișierele așteptate sunt acolo, niciunul n-a fost adăugat, niciunul n-a fost modificat. Pe un bundle de test pe care l-am semnat și apoi l-am retușat, comanda răspunde a sealed resource is missing or invalid, apoi file modified: urmat de fișierul schimbat. Opțiunea --deep servește aici la verificare, niciodată la semnare: pe forumul Apple, un inginer din echipa de suport o descurajează pentru semnare.
3. Ce profil e inclus?
security cms -D -i ipa/Payload/*.app/embedded.mobileprovision -o profile.plist
plutil -extract ProvisionedDevices xml1 -o - profile.plist
plutil -extract ExpirationDate raw profile.plist
Sunt comenzile din nota TN3125 despre profiluri. Un profil App Store nu conține nicio listă de dispozitive: a doua linie trebuie să răspundă Could not extract value. Dacă afișează identificatori de dispozitive, e un profil ad hoc, cel al unui build intern. A treia îți dă data de expirare a profilului.
4. Extensiile și framework-urile sunt semnate la fel?
find ipa/Payload/*.app \( -name "*.appex" -o -name "*.framework" \) \
-exec sh -c 'echo "$1"; codesign -dvv "$1" 2>&1 | grep -m1 "^Authority="' _ {} \;
Fiecare componentă trebuie să afișeze aceeași linie Authority ca aplicația. Dacă una diferă, pe ea o menționează mesajul.
Dacă vrei să verifici înainte de trimitere, TN3161 recomandă altool, aceeași verificare ca butonul Validate App din Xcode. Are nevoie de o cheie API App Store Connect, al cărei fișier AuthKey_<KEY_ID>.p8 se pune în ~/.appstoreconnect/private_keys:
xcrun altool --validate-app -f MyApp.ipa -t ios --api-key <KEY_ID> --api-issuer <ISSUER_ID>
Rezolvarea cu EAS, cauză cu cauză
Un build intern sau de dezvoltare trimis la Apple
Conform documentației Expo despre eas.json, build-urile cu "distribution": "internal" nu sunt semnate pentru store-uri: folosesc un profil ad hoc. Doar un profil cu distribuție store, valoarea implicită, produce un build care poate fi trimis. Cel mai sigur e să trimiți build-ul pe care tocmai l-ai construit, fără să-l alegi de mână:
eas build --platform ios --profile production --auto-submit
Dacă folosești eas submit --path, verifică dacă fișierul e chiar ultimul tău build store. Pe forumul Apple, o echipă a descoperit că unealta ei de trimitere retrimitea de fiecare dată un IPA vechi, versionat în git.
O extensie uitată
Fiecare extensie e un bundle separat, cu identificatorul și profilul ei. Într-un proiect Expo cu generare nativă, pagina Expo despre extensiile iOS cere să le declari în extra.eas.build.experimental.ios.appExtensions. Așa EAS CLI le cunoaște înainte de build și le creează sau le validează credențialele. Majoritatea bibliotecilor care adaugă o extensie scriu declarația asta pentru tine, prin plugin-ul lor.
Solys, alarma mea cu mini-jocuri, include patru extensii: un widget și trei extensii Screen Time. Toate patru sunt declarate în configurație, iar EAS are un profil pentru fiecare. O extensie adăugată de mână în Xcode, fără declarația asta, e candidatul ideal pentru o cale în .appex în mesaj.
Un certificat revocat sau expirat
Pagina Apple despre certificate e clară: nu mai poți trimite un build semnat cu un certificat expirat sau revocat. Un build deja trimis, dar încă neînaintat la review, poate chiar să treacă în Invalid Binary dacă certificatul lui a fost revocat.
Codul din EAS CLI 24.10.0 arată când trece cazul ăsta neobservat:
- Build interactiv, conectat la Apple: EAS își compară certificatul cu cele încă valide la Apple. Dacă nu mai e acolo, afișează „Current distribution certificate is no longer valid in Apple Dev Portal” și îți propune să alegi sau să creezi unul.
- Build interactiv, fără conectare: EAS verifică doar datele și te avertizează că sare peste validarea la Apple.
- Build neinteractiv, în integrare continuă: EAS afișează „Distribution Certificate is not validated for non-interactive builds” și semnează oricum.
În integrare continuă, EAS nu reverifică certificatul în sine: o revocare se poate vedea abia la trimitere. Rezolvarea: pornește un build interactiv conectat la contul tău Apple, sau treci prin eas credentials --platform ios, meniul Build Credentials, apoi „Distribution Certificate: Add a new one to your account”. Profilurile care conțineau vechiul certificat devin invalide: EAS le verifică la următorul build și le reface. Limitele Apple privind numărul de certificate sunt detaliate în ghidul despre certificatul de dezvoltator iOS.
Un binar modificat după semnare
Semnătura sigilează codul, resursele și metadatele prin hash-uri, cum explică nota TN3126 de la Apple. Dacă dezarhivezi IPA-ul ca să schimbi un fișier și apoi îl rearhivezi, rupi sigiliul. Fă modificarea în configurație, apoi refă build-ul. Nu retușa niciodată IPA-ul produs de EAS.
Aceeași logică pentru cod pus în locul greșit, de exemplu într-o arhivă .zip: App Store nu-l poate re-semna. Apple descrie locațiile permise în pagina despre conținutul unui bundle.
Credențiale locale
Dacă profilul tău EAS folosește "credentialsSource": "local", fișierul credentials.json indică un certificat .p12 și un profil .mobileprovision. Verifică profilul cu comenzile de la pasul 3: trebuie să fie de tip App Store și să conțină certificatul tău de distribuție actual. Și ține fișierele astea în afara repository-ului, ca pe cheile API ale unei aplicații Expo.
Când totul e valid
Mai rămâne un caz frustrant. Pe forumul Apple, unii dezvoltatori văd eroarea dispărând când retrimit același build, fără să schimbe nimic. În august și septembrie 2026, două thread-uri descriu opusul: certificat și profil noi, semnătură verificată, eșec persistent. Într-unul dintre ele, un inginer Apple recomandă un raport de bug cu IPA-ul, analiza semnăturii și logurile din Transporter, apoi un contact cu App Store Connect.
Așa că retrimite o dată cu un număr de build nou. Dacă eroarea revine deși cele patru comenzi ies curate, raportează-o la Apple cu elementele astea. Nu-ți revoca certificatul din reflex: nu rezolvă nimic în plus și îți invalidează profilurile.
Trimite-ți build-ul fără surprize neplăcute
O semnătură se verifică în două minute înainte de trimitere. Celelalte verificări automate cer aceeași rigoare, cum ar fi ITMS-90725 pentru versiunea de Xcode sau ITMS-90683 pentru textele de permisiune. Tot parcursul de la build la trimitere, fără Mac, îl găsești în publică o aplicație iOS fără Mac.
Respingerile de la un reviewer cer mai multă detașare: pașii sunt pe pagina aplicație respinsă de Apple, iar motivele cele mai frecvente sunt în de ce respinge Apple o aplicație.
Ca să-ți verifici dosarul înainte de trimitere, Review Kit adună checklist-ul meu și skill-uri Claude Code care îți recitesc proiectul. Dacă trimiterea ți se blochează și vrei o privire umană, rezervă o oră cu mine: reluăm proiectul tău pe video, pe ecranul tău. Iar dacă îți pregătești prima lansare, fă-ți diagnosticul gratuit: îți pune pașii în ordine și îți trimite checklist-ul meu anti-respingere Apple.
Întrebări frecvente
ITMS-90034 e o respingere de la App Review?
Nu. E o validare automată făcută la trimiterea build-ului, înainte de TestFlight și înainte de orice review. Nimeni nu ți-a deschis aplicația: Apple a constatat doar că semnătura ei nu vine de la un certificat de distribuție valid.
Trebuie să-mi revoc certificatul de distribuție ca să rezolv ITMS-90034?
Doar dacă e compromis sau deja invalid. Dacă revoci un certificat care funcționează, îți invalidezi profilurile fără să rezolvi cauza. Începe prin a citi semnătura IPA-ului cu codesign, apoi rezolvă ce indică arborele de decizie.
Se poate rezolva ITMS-90034 cu un update la distanță?
Nu. Semnătura face parte din binar, iar build-ul respins nu va fi niciodată utilizabil. Trebuie să produci un binar nou, semnat corect, și să-l retrimiți.
De ce menționează eroarea widget-ul meu și nu aplicația?
Pentru că widget-ul e un bundle separat, semnat cu propriul profil. Dacă nu e declarat în EAS sau a fost semnat altfel, pică toată trimiterea. Declară extensia în configurație și refă build-ul.
Ce fac dacă codesign spune că totul e valid?
Verifică și profilul inclus și fiecare extensie. Dacă totul e curat, retrimite o dată cu un număr de build nou. Dacă eroarea persistă, trimite un raport la Apple cu IPA-ul, ieșirea din codesign și logurile de trimitere.

