Read the second half of the message carefully, because that is where the useful part is: "with a private key was found". Xcode is not telling you that no distribution certificate exists. It is telling you that no usable signing identity exists on this machine. An identity is a certificate plus the private key that was generated alongside its CSR, and both halves have to be present in the same keychain.
The name in the message is also worth noting. Apple stopped issuing certificates called iOS Distribution years ago and replaced them with the unified Apple Distribution type, but Xcode's build settings still refer to the old label for iOS product types. Seeing iOS Distribution in the error does not mean you need a legacy certificate. A current Apple Distribution certificate satisfies it.
Find out what you actually have
One command settles the question. It lists only identities where both the certificate and the private key are present, which is exactly the set Xcode is searching.
# Valid signing identities, key included security find-identity -v -p codesigning # Everything, including expired and unusable entries security find-identity -p codesigning
If the verbose-valid list is empty, or contains only Apple Development entries, you have found the problem. If the second command shows a distribution certificate that the first one does not, the certificate is there without its key, and you want the missing-private-key page instead.
The causes, most common first
- You downloaded the .cer from the portal
- A .cer is the certificate only. The private key stays on the Mac where the CSR was generated and is never sent to Apple. Importing a .cer on a second machine gives you a certificate with no key, which does not satisfy this check. You need a .p12 exported from the original machine.
- Xcode is signed in as the wrong team
- The message quotes a Team ID. If that Team ID is not the one that owns your distribution certificate, the search will fail even though a perfectly good identity is sitting in your keychain. Check the target's Signing and Capabilities tab against Xcode, Settings, Accounts.
- The certificate expired or was revoked
- Expired identities are filtered out of the valid list, so the symptom is identical to having nothing at all. Revocation by a teammate doing a fastlane match nuke is a classic version of this.
- The key is in a keychain Xcode is not searching
- Common after a CI script creates a dedicated keychain, or after importing into the System keychain. The keychain has to be in the search list for the user running the build.
- Your account cannot create distribution certificates
- The Developer role on a team account generally cannot. A free Apple ID cannot at all: it only signs for local development. Either way, nothing will ever appear in your list until an Admin or the Account Holder acts.
Fixing it on your Mac
- 1
Ask whoever has the distribution certificate for a .p12
In Keychain Access on their machine: My Certificates, right-click the Apple Distribution entry, Export, and set a password. Send the password over a different channel than the file.
- 2
Import it and check the identity appears
Double-click the .p12, or use security import. Then re-run security find-identity -v -p codesigning. The identity has to show up here before Xcode will ever see it.
- 3
If nobody has the key, issue a new certificate
Generate a CSR, create an Apple Distribution certificate in the portal, and download it on the machine that made the CSR. Apple caps how many distribution certificates a team may hold at once, so you may have to revoke an old one first.
- 4
Regenerate every profile that referenced the old certificate
A new certificate is not retroactively added to existing provisioning profiles. Any profile created before it needs regenerating, or your next build fails on a different error about the profile not including the certificate.
Fixing it on CI
A fresh runner has an empty keychain, so this error on CI nearly always means the import step did not run, ran into the wrong keychain, or ran into a keychain that was never added to the search list. Importing is three separate things: create, import, and make visible. Missing the third is the quiet failure, because nothing errors until xcodebuild goes looking.
KEYCHAIN=build.keychain security import dist.p12 -k "$KEYCHAIN" -P "$P12_PASSWORD" \ -T /usr/bin/codesign -T /usr/bin/security # Without this, xcodebuild searches the login keychain and finds nothing security list-keychains -d user -s "$KEYCHAIN" login.keychain # The check that should pass before you call xcodebuild security find-identity -v -p codesigning "$KEYCHAIN"
Add that last command to your pipeline permanently. It turns a confusing Xcode error late in the build into an obvious failure two seconds after the import step, and it costs nothing.