IPA signing

Check an IPA’s Code Signature and Signing Certificate

Verify an IPA's code signature, identify who signed it, and compare the signed app with its embedded provisioning authorization.

Inspecting an IPA must answer three separate questions:

  1. Does the code signature cryptographically verify?
  2. Who—or which Apple Developer team—signed the app?
  3. Does the embedded provisioning authorization match the intended app, device, entitlements, and distribution workflow?

A green answer to one question does not automatically answer the other two. A valid signature can belong to the wrong team or accompany an incompatible profile. A profile can list the target device while the signed bundle is damaged. And signature validity does not establish that an unknown artifact is trustworthy.

Preserve provenance and checksum first

Before extraction, record where the IPA came from, who authorized inspection, its intended distribution method, filename, version/build if known, and a checksum:

shasum -a 256 ClientBuild.ipa
mkdir ClientBuild-unpacked
unzip -q ClientBuild.ipa -d ClientBuild-unpacked

APP_PATH=$(find ClientBuild-unpacked/Payload \
  -maxdepth 1 -name '*.app' -print -quit)

Keep the original IPA unchanged. Extraction for inspection does not alter it; editing files inside the app and repackaging them invalidates the existing signature.

If provenance is unknown, preserve the file as untrusted evidence. Do not install it, re-sign it, or treat a successful codesign result as proof that the software is safe or authorized by the app owner.

Question 1: does the main app signature verify?

Display the signature and run a strict verification against the extracted main app:

codesign --display --verbose=4 "$APP_PATH" 2>&1
codesign --verify --strict --verbose=4 "$APP_PATH"

The display output can expose the code-signing identifier, signature format, authority chain, team identifier, and sealed-resource context. Verification checks whether the bundle currently satisfies its code-signing requirements on the inspection Mac.

A nonzero verification result is evidence of a signing or artifact-integrity problem. Preserve the complete output and checksum before changing anything. Possible causes include modified signed content, damaged packaging, an incomplete signing pass, or a nested-code inconsistency.

Local verification is necessary evidence, not a guarantee of successful installation on every iPhone. iOS still evaluates distribution method, profile authorization, entitlements, device eligibility, platform compatibility, and other device-side requirements.

Question 2: who signed the app?

Read the main bundle identity and signed entitlements alongside the verbose signature output:

plutil -extract CFBundleIdentifier raw \
  -o - "$APP_PATH/Info.plist"

codesign --display --entitlements :- "$APP_PATH" \
  > /tmp/app-entitlements.plist 2>/dev/null

plutil -p /tmp/app-entitlements.plist

Record:

Do not infer ownership from a certificate common name alone. Compare team, application identifier, certificate identity, bundle identifier, and the app owner’s release records. A certificate can be cryptographically valid yet belong to a team that does not own the intended App ID.

Question 3: does the embedded profile authorize this app?

For an app with embedded.mobileprovision, decode the exact embedded profile:

security cms -D \
  -i "$APP_PATH/embedded.mobileprovision" \
  -o /tmp/client-profile.plist

plutil -extract Name raw -o - /tmp/client-profile.plist
plutil -extract UUID raw -o - /tmp/client-profile.plist
plutil -extract ExpirationDate raw -o - /tmp/client-profile.plist
plutil -extract TeamIdentifier xml1 -o - /tmp/client-profile.plist
plutil -extract Entitlements.application-identifier raw \
  -o - /tmp/client-profile.plist
plutil -extract DeveloperCertificates xml1 \
  -o /tmp/profile-certificates.plist \
  /tmp/client-profile.plist

Apple describes DeveloperCertificates as the profile’s authorization for who may sign covered code. The app’s signing certificate must be represented in that authorization, and the signing machine needs the matching private key when producing a new artifact. The profile also constrains app identity, validity, device location where applicable, and entitlement claims.

Compare the app and profile as one authorization set:

Inspect a Provisioning Profile Inside an IPA owns the field-by-field profile evidence workflow. Profile and Signing Certificate Mismatch owns exact certificate/profile disagreement. Bundle ID and Profile Mismatch owns identity and entitlement mismatch.

Inspect nested signed code explicitly

The main app can verify while a nested extension, framework, or app is inconsistent. Enumerate nested code instead of assuming the outer result proves every component:

find "$APP_PATH" -type d \
  \( -name '*.appex' \
     -o -name '*.framework' \
     -o -name '*.app' \) -print

For each signed component, run a deliberate inspection:

codesign --display --verbose=4 "PATH_TO_COMPONENT" 2>&1
codesign --verify --strict --verbose=4 "PATH_TO_COMPONENT"
codesign --display --entitlements :- "PATH_TO_COMPONENT" 2>/dev/null

For each nested .appex or .app carrying its own provisioning profile, repeat the security cms and profile comparison. A framework normally participates in code signing but does not use an iOS device-list profile in the same way as an app extension.

Record each component’s bundle identifier, signing team, authority, entitlements, verification result, and profile context where present. A main-app success does not prove a notification extension, widget, App Clip, embedded framework, or nested app was signed and sealed correctly.

Be careful with --deep

Do not use --deep as a generic signing repair method. Apple Developer Technical Support explicitly warns against deep signing because complex bundles should be signed as deliberate code items in the correct inside-out order.

For verification, a recursive --deep check can be a supplemental scan, but it is not a substitute for identifying and inspecting each nested signed component. It can tell you that something under the outer bundle fails without producing the release evidence needed to understand each target’s bundle ID, entitlements, certificate, and profile.

Never combine --deep, --force, and a signing identity as an experimental “fix” on the only copy of an IPA. Preserve the original and use an authorized workflow that understands the bundle structure.

What signature verification proves—and does not prove

A successful codesign --verify result proves that the inspected code currently satisfies the local code-signing verification requested by that command. It does not prove:

Conversely, a recognizable profile name or target UDID does not prove the code signature is intact.

Decision table

Evidence Next action
Signature invalid Preserve the IPA, checksum, and command output. Investigate modification, packaging, nested code, or signing-chain failure before any repair.
Signature valid; target device absent Treat it as a profile/device authorization problem. Verify the exact embedded profile and produce a replacement artifact.
Signature valid; certificate and profile disagree Follow the certificate/profile diagnostic. Select or regenerate compatible signing inputs, then rebuild or authorizedly re-sign.
Signature valid; authorization appears correct; install still fails Preserve the artifact and move to installation, integrity, delivery, OS compatibility, or device-state diagnostics.
Artifact provenance unknown Do not install or trust it merely because the signature verifies. Establish app-owner authorization and source first.

For an integrity-specific device alert, continue with App Integrity Could Not Be Verified. For a generic installation failure, use Unable to Install App. If the owner authorizes a replacement and the inputs are compatible, How to Re-sign an IPA Safely explains when re-signing is appropriate and when rebuilding is safer.

Close the release-evidence loop

After a rebuild or authorized re-sign:

  1. checksum the replacement IPA;
  2. extract it into a new directory;
  3. verify the main app and every nested signed component;
  4. compare the embedded profile, team, App ID, certificate, entitlements, expiry, and devices;
  5. install the exact checksummed artifact on a representative intended device; and
  6. replace the delivery object without leaving the old IPA behind the same ambiguous link.

Run the browser-local iOS Distribution Checklist before client handoff. Do not upload Apple credentials, P8/P12 files, private keys, or untrusted IPAs to a public diagnostic service.

Related next steps

Next step. Preserve the original artifact and route the first proven mismatch to the focused repair guide. Join Early Access.

Sources