When an iOS build refuses to install, the error rarely says which part of the signing is wrong. “Unable to install”, “The executable was signed with invalid entitlements” (0xe8008016), “A valid provisioning profile for this executable was not found” (0xe8008015): each of these comes down to a handful of facts that live in two places, the provisioning profile and the code signature of the executable. On a Mac you would read them with:
security cms -D -i embedded.mobileprovision
codesign -d --entitlements :- Payload/MyApp.app
This page reads the same data from the file itself, in your browser, and puts the two side by side. It is meant for checking your own builds and profiles: why a build will not install on a test device, which profile an exported IPA actually contains, whether the push entitlement says development or production.
What you can drop here
- Provisioning profiles:
.mobileprovision(iOS, iPadOS, tvOS, watchOS, visionOS) and macOS.provisionprofile. Profiles are CMS SignedData wrapping an XML plist; both the definite-length encoding current profiles use and the BER indefinite-length form found in older ones and produced byopenssl cms -streamare read. The XML thatsecurity cms -Dprints works too. - Builds: an
.ipa, a zipped.app(Finder’s “Compress”), or a zipped.xcarchive. Only the ZIP central directory is read up front; then just the app’sInfo.plist,embedded.mobileprovision,PrivacyInfo.xcprivacy, each extension’sInfo.plist, and the main executable. A multi-gigabyte IPA is not loaded into memory; ZIP64 archives are supported. - Single files:
Info.plistin XML or binary (bplist00) form, an.entitlementsfile, aPrivacyInfo.xcprivacy, or a bare Mach-O executable (thin or universal) to list the entitlements in its signature.
What you get
The profile: name, App ID name, App ID (application-identifier, with the team prefix), UUID, team ID and name, platform, creation and expiry date with days left, whether Xcode manages it, the full Entitlements dictionary, the list of provisioned device UDIDs with a search box, the developer certificates (common name, team from the OU field, SHA-1, validity) and the certificates of the CMS signer.
The profile type is not stored in the file. It is derived the way Xcode and fastlane label profiles:
| Profile contains | Type |
|---|---|
ProvisionsAllDevices = true |
Enterprise (In-House); on macOS, Developer ID |
ProvisionedDevices and get-task-allow = true |
Development |
ProvisionedDevices, get-task-allow = false |
Ad Hoc |
| neither | App Store (Mac App Store on macOS) |
The app: bundle id, version and build, minimum OS, SDK and Xcode version, device family, supported platforms, URL schemes, required device capabilities, App Transport Security settings, every NS…UsageDescription with its text, the privacy manifest (tracking flag, tracking domains, collected data types, and each required-reason API category with its reason codes such as CA92.1), embedded frameworks and dylibs (with a flag for those that ship their own privacy manifest) and app extensions with their bundle ids and extension points.
The executable: architecture slices (arm64, arm64e, …) with platform and minimum OS from LC_BUILD_VERSION, the cryptid of LC_ENCRYPTION_INFO_64 (1 means an App Store download, whose code is encrypted), the signing identifier and team from the CodeDirectory, whether the signature is ad hoc, the signing certificate, and the entitlements from both blobs of the code signature: the XML plist (slot 5) and the DER form (slot 7) that iOS 15 and later evaluate. If the two disagree, the page says so.
The checks
With a build that contains its profile, the page runs the checks behind the usual install failures:
- Expiry: an expired profile, or one that expires within 30 days.
- Bundle id:
CFBundleIdentifiermust match the profile’s App ID after the team prefix, where*is a wildcard (TEAMID.com.example.*). - Entitlements: every entitlement the executable is signed with must be allowed by the profile. Strings and arrays may use the profile’s wildcards (
TEAMID.*for keychain groups,*for associated domains);get-task-allowtrue in an App Store or Ad Hoc build,aps-environmentdevelopmentagainst aproductionprofile, a keychain group of another team, or a capability the profile does not have (iCloud, App Groups) each fail with the key and both values. On macOS, sandbox and hardened-runtime entitlements undercom.apple.security.need no profile and are shown as such. - Team: the team of the signed
application-identifier(or the CodeDirectory team) against the profile’s team. - Signing certificate: the certificate in the code signature must be one of the profile’s
DeveloperCertificates, compared by SHA-1. This is the “profile doesn’t include the signing certificate” case. - Extensions: each
.appexbundle id must start with the app’s bundle id plus a dot.
To check a device, paste its UDID into the box under the device list. The comparison runs on the page; nothing is sent.
How we checked it
The fixtures are synthetic and built on macOS 27.0.1 with Xcode 27.0 (iPhoneOS 27.0 SDK) and OpenSSL 4.0.3 by test/ios/build.sh: a throwaway CA, a “developer” certificate and a profile-signing certificate; five hand-written profiles (development, Ad Hoc in BER indefinite-length form, an expired App Store profile, enterprise with a * App ID, and a macOS development profile) signed with openssl cms -sign -binary -nodetach; and a small C program compiled for arm64 and arm64e, signed with codesign --entitlements --generate-entitlement-der and zipped as Payload/Fixture.app. A second build has the wrong bundle id, get-task-allow true and development push against the App Store profile, another team’s keychain group, an iCloud entitlement the profile lacks, a certificate the profile does not list, a misnamed widget extension, and cryptid set to 1 in one slice. The unit tests compare the results with security cms -D, plutil, codesign -d --entitlements (XML and --der), lipo -archs and otool -l. We also ran the profile parser over the 20 real profiles Xcode had downloaded on our Mac: name, UUID, team, expiry, device list, entitlements and certificate SHA-1s matched security cms -D for all 20 (none of that data is in this site or its tests).
Two things that cost us time while building the fixtures. plutil -convert json rejects a decoded profile with “Invalid object in plist for JSON format”, because profiles contain <date> and <data> values JSON cannot hold, so the build script falls back to plutil -p for its reference output. And codesign -dv does not print the Authority= lines at all; we only saw which certificate had signed a build with -dvvv. If you run security cms -D -i on the .ipa itself instead of the profile inside it, it fails with security: failed to add data to decoder: UNKNOWN (-8183(d): unzip first, or drop the IPA here.
How your files stay private
- Files are read in a Web Worker on your device. Nothing is uploaded, and the page has no server component.
- UDIDs, team ids, bundle ids and certificate details are shown on the page only. The UDID you paste is compared in the page and not stored.
- Analytics events contain only coarse values: success or an error code, the kind of file (
profile,ipa, …), the derived profile type, the number of failed checks as a bucket, and size and duration buckets. The full list is on the privacy page. - The tool page shows no ads.
How to verify this yourself
- Open your browser’s developer tools on the Network tab and drop an IPA. The only requests are page assets and the worker script
/_astro/ios-worker-*.js. - Or load the page, go offline, and drop a profile. It still works.
- The worker runs under the Content Security Policy
default-src 'none'; script-src 'self'; connect-src 'none', so the browser blocks any request it tries to make. - Compare with your own tools:
security cms -D -i file.mobileprovision,codesign -d --entitlements :- MyApp.appandotool -l MyApp | grep -A4 LC_ENCRYPTIONprint the same values.
Limitations
- Nothing is verified cryptographically. Neither Apple’s signature on the profile nor the app’s code signature is checked; a profile or binary edited after signing looks normal here but will be rejected by the device.
- Nothing is decrypted, re-signed or modified. For App Store downloads, only the load commands and the signature are read.
- App extensions are listed with their bundle ids; their own profiles and entitlements are not cross-checked. Drop an extension’s
embedded.mobileprovisionon its own to read it. - The entitlement rules cover the common cases (exact values,
*wildcards, arrays as subsets). Apple’s installer has special cases this page does not model, for example entitlements granted to Apple-signed code only. - The device list comes from the profile. Whether a device is also registered and enabled in your developer account is not visible in the file.
FAQ
Which profile is inside my IPA?
The one at Payload/<App>.app/embedded.mobileprovision, which this page shows under Provisioning profile, with its name and UUID. Xcode’s export may pick a different profile than the one you expected, especially with automatic signing; the UUID tells you which one it was.
The profile allows the entitlement, but the install still fails.
Look at the Signed with column: the value in the binary must be allowed, not only the key. aps-environment development in a build signed with a distribution profile is the classic case, as is a keychain group with the wrong team prefix. Also check the signing certificate row: a profile regenerated after a certificate renewal no longer lists the old certificate.
How do I find my device’s UDID?
Connect it and look in Finder (click the text under the device name until the UDID appears) or in Xcode under Window > Devices and Simulators.
Related tools and guides
- Native binary overview: the full load commands, segments, symbols and libraries of a Mach-O executable
- X.509 certificate decoder: every field and extension of a certificate
- Free native binary analysis with REA and Ghidra
- What’s Inside a .mobileprovision, and How to Check It: decode a profile with
security cms,opensslandplutil, and compare it withcodesign -d --entitlementson a synthetic IPA