Mobile Development Bug Report

Xcode "Provisioning profile doesn't include signing certificate" after a certificate renewal

Our yearly Apple distribution certificate expired, we created a new one, and every archive failed: the provisioning profile doesn't include the signing certificate. Profiles embed the certificates they trust. How we regenerated profiles, cleaned keychains and verified signing identities.

Marcus Lindqvist Marcus Lindqvist @marcuslindqvist · November 04, 2024 · 7 min read · 41 Views
Banner: Xcode provisioning profile doesn't include signing certificate after renewing the distribution cert

Our iOS distribution certificate was about to expire. Following the reminder email, a developer created a new Apple Distribution certificate in the Apple Developer portal, installed it on the build Mac and revoked the old one to keep things tidy. The next archive for TestFlight failed in Xcode:

Provisioning profile "Shop App Store" doesn't include signing certificate "Apple Distribution:
Example Ltd (A1B2C3D4E5)".

On the CI machine, xcodebuild said the same thing in a longer form:

error: Provisioning profile "Shop App Store" doesn't include signing certificate "Apple Distribution:
Example Ltd (A1B2C3D4E5)". (in target 'Shop' from project 'Shop')
** ARCHIVE FAILED **

What is in the profile?

A provisioning profile is a signed plist. You can decode it and list the certificates it contains:

security cms -D -i ~/Library/MobileDevice/Provisioning\ Profiles/0f3c...mobileprovision > profile.plist
/usr/libexec/PlistBuddy -c 'Print :DeveloperCertificates' profile.plist | head -3
plutil -extract DeveloperCertificates.0 raw -o - profile.plist | base64 -d | \
  openssl x509 -inform der -noout -subject -enddate -serial
subject=UID=A1B2C3D4E5, CN=Apple Distribution: Example Ltd (A1B2C3D4E5), OU=A1B2C3D4E5, O=Example Ltd, C=GB
notAfter=Nov  6 09:14:22 2024 GMT
serial=5F2A...

The profile contained the old certificate, with the old serial number. And the identities in the keychain:

security find-identity -v -p codesigning
  1) 9A7F...  "Apple Distribution: Example Ltd (A1B2C3D4E5)"    <- new
  2) 3C1D...  "Apple Distribution: Example Ltd (A1B2C3D4E5)" (CSSMERR_TP_CERT_REVOKED)
     2 valid identities found

Same name, different certificates. The new identity was valid; the profile did not include it.

What we ruled out

  • The wrong team or bundle id. The profile's TeamIdentifier and application-identifier matched.
  • Missing private key. find-identity listed the new certificate as a valid identity, which requires the private key to be present.
  • An expired WWDR intermediate certificate. That produces "unable to build chain" errors, not this one.

Root cause

Each provisioning profile embeds the specific certificates allowed to sign with it. Creating a new distribution certificate does not update existing profiles, and revoking the old certificate made the profiles invalid. Xcode found the new identity in the keychain, matched it by name, then rejected it because the profile only trusted the old certificate.

Certificates, identities and profiles

Item Contains Lives in
Signing certificate Public key, issued by Apple, valid for a year Developer portal and keychain
Signing identity Certificate plus its private key Keychain of the build machine
Provisioning profile App id, team, entitlements, allowed certificates, devices (for development/ad hoc) Portal, then ~/Library/MobileDevice/Provisioning Profiles

Codesigning needs an identity whose certificate is listed in the profile. Profiles do not refer to "the current distribution certificate"; they list exact certificates. So renewing a certificate requires regenerating every profile that used the old one.

Revocation versus expiry

An expired certificate stops new signing but does not affect apps already on the App Store. Revoking a distribution certificate also does not affect App Store apps, but it invalidates profiles that include it, and for enterprise distribution it breaks installed apps. Revoking early, before new profiles exist, turns a routine renewal into a blocked release.

Who is affected

Teams renewing distribution or development certificates with manually managed profiles, teams sharing one build machine or CI where profiles are installed by hand, and anyone who revokes a certificate before regenerating the profiles that depend on it. Xcode's automatic signing handles this for local builds, but CI setups with manual profiles do not.

Environment

Component Version / value
Xcode 16.1
Signing Manual signing for release builds, profiles installed on CI
Certificates Apple Distribution, old one revoked after the new one was created
Profiles App Store profiles for the app and its notification service extension

Regenerate the profiles

In the Apple Developer portal, under Profiles, each App Store profile for the app and its extensions was edited to include the new certificate and saved, which produces a new profile with a new UUID. The same can be done through the App Store Connect API or fastlane:

fastlane sigh --app_identifier com.example.shop --force
fastlane sigh --app_identifier com.example.shop.NotificationService --force

Install the new profiles and remove the old ones

rm ~/Library/MobileDevice/Provisioning\ Profiles/0f3c*.mobileprovision    # old UUIDs
open ShopAppStore.mobileprovision                                         # installs the new one

Old profiles with the same name confuse Xcode's manual selection, so deleting them matters. On CI, the profiles directory is rebuilt from scratch in each job.

Clean the keychain

The revoked identity stayed in the keychain and appeared next to the new one with the same name, which can make codesign pick the wrong one when identities are referenced by name. We deleted it:

security delete-certificate -Z 3C1D...    # SHA-1 of the revoked certificate

And in the Xcode project, release signing now refers to the identity by its SHA-1 hash in the export options, not by name.

Archive again

xcodebuild -workspace Shop.xcworkspace -scheme Shop -configuration Release \
  -archivePath build/Shop.xcarchive archive
xcodebuild -exportArchive -archivePath build/Shop.xcarchive \
  -exportOptionsPlist ExportOptions.plist -exportPath build/export

Extensions and other targets

The app had a notification service extension with its own bundle id and its own App Store profile. We regenerated that profile too; forgetting an extension produces the same error, but naming the extension target, which is easy to misread as the main app failing again. List every target's bundle id before a renewal:

xcodebuild -workspace Shop.xcworkspace -scheme Shop -showBuildSettings \
  | grep -E "PRODUCT_BUNDLE_IDENTIFIER|PROVISIONING_PROFILE_SPECIFIER"

Alternatives and trade-offs

  • Automatic signing in Xcode. Handles regeneration for local builds; CI needs the same account access, which many teams avoid.
  • fastlane match keeps certificates and profiles in an encrypted repository shared by the team and CI, and regenerates them together. We moved to it the following month; a separate post covers that pipeline.

How to test the fix

security cms -D -i build/export/Payload/Shop.app/embedded.mobileprovision | grep -A1 UUID
codesign -dvv build/export/Payload/Shop.app 2>&1 | grep -E "Authority|TeamIdentifier"

The authority chain should show the new Apple Distribution certificate, and the embedded profile should be the new UUID.

Confirming in production

The build uploaded to App Store Connect, passed processing and was distributed to TestFlight testers the same day. The app already in the App Store kept working throughout, as expected.

Updating CI

CI installed profiles from a shared drive. We replaced the files there, bumped the profile names in the export options, and ran a full archive and export on CI before telling the team the renewal was complete. A renewal is only done when CI can produce an uploadable build.

Regression guard

  • A calendar reminder 30 days before certificate expiry, with a runbook: create the new certificate, regenerate all profiles, update CI, verify a build, and only then revoke the old certificate if needed.
  • A CI step that prints the certificate serial in each profile and fails if it does not match the signing identity, with a readable message.

Security implications

Distribution certificates and their private keys let anyone sign builds as your team. Keep them out of personal keychains where possible, store them encrypted for CI, and limit who can create or revoke them in the developer account. Revoking is the right action after a key compromise; for routine renewal, it is usually unnecessary.

Prevention checklist

  • Remember that profiles embed specific certificates; renewals require new profiles.
  • Do not revoke the old certificate until new profiles are installed and tested.
  • Remove old profiles and revoked identities from build machines.
  • Reference signing identities by hash in CI.
  • Consider fastlane match or automatic signing to keep certificates and profiles in sync.

References

Add a label

#ios #xcode #code-signing #provisioning-profile #app-store
Written by
Marcus Lindqvist @marcuslindqvist

I have spent most of the last decade on mobile teams, first native iOS, then React Native and Flutter for companies that needed one team to ship to both stores. The work I enjoy most is the unglamorous part between "it runs on my phone" and "it is live for a million users": code signing, release builds, store review, crash triage and background behaviour that differs on every Android vendor. Most of the bugs I write about here cost a release day. Each post shows the exact error, what we ruled out, the fix and how we proved it in the next build, so you can get your own release out the door sooner.

Advertisement

Comments

0

No comments yet. Be the first to comment!

Sign In to post a comment.