Xcode says expired or revoked because, from the device's point of view, the two are indistinguishable: the certificate that signed this build is not currently valid. From your point of view they are entirely different problems with different blast radii, so the first job is working out which one you are looking at.
Expiry is a clock running out. Nothing was taken away from you, the certificate simply reached the end of its validity period and stopped being usable for new signatures. Revocation is somebody, or something, actively cancelling it, and revocation propagates: the portal marks every profile that referenced that certificate as invalid.
Which one is it
# Identities the keychain will offer, with expired ones flagged security find-identity -v -p codesigning # Exact validity window of a certificate file openssl x509 -inform DER -in distribution.cer -noout -subject -dates # Or from an identity already in the keychain security find-certificate -c "Apple Distribution: Acme Corp (ABCDE12345)" \ -p | openssl x509 -noout -subject -dates
If notAfter is in the past, it expired and there is nothing more to investigate. If the dates are fine and it still fails, it was revoked, and the portal is where you confirm that: a revoked certificate disappears from the Certificates list rather than appearing with a warning, which is itself a useful signal.
- Apple Development and Apple Distribution
- One year each on the standard Apple Developer Program. A year is short enough to forget about and long enough that nobody remembers how they fixed it last time.
- Enterprise Program distribution certificates
- Three years, with In-House provisioning profiles expiring after one. The longer certificate makes it easy to assume nothing expires, right up until a profile does.
- Developer ID Application
- Five years, for Mac apps distributed outside the App Store. Signatures made with a trusted timestamp stay valid after the certificate expires, which is why --timestamp matters for Developer ID signing.
Who revoked it
Worth five minutes before you reissue, because an unexplained revocation that you paper over will happen again.
- A teammate cleaning up the portal. Certificates with no obvious owner get revoked by whoever is tidying, and the person who owned the private key finds out at build time.
- Xcode, on your behalf. Xcode will offer to revoke an existing certificate when your account is at its limit and you ask it to create another one. It does ask, but it asks in a dialog nobody reads.
- A departing team member's access being removed, which can take their certificates with it.
- A lapsed Apple Developer Program membership. Everything under the account stops being valid until the renewal goes through.
- A deliberate revocation after a private key leaked. If that is the reason, reissuing quietly is the wrong response.
Rotating without breaking the profiles you still need
A provisioning profile embeds the list of certificates it accepts. That list is a snapshot taken when the profile was generated, so issuing a new certificate does not add it to existing profiles, and every profile has to be regenerated before it will accept builds signed with the new certificate. This ordering is what separates a rotation from an outage.
- 1
Issue the new certificate before revoking the old one
A profile can list more than one certificate. With both valid at once, a regenerated profile accepts builds signed by either, and you can migrate machines one at a time instead of all at once.
- 2
Regenerate every profile that referenced the old certificate
Development, Ad Hoc, App Store, In-House. Each one separately, including the profiles for extensions and watch apps. Regeneration gives each profile a new UUID, so anything pinning a UUID needs updating too.
- 3
Roll the new identity out to every signing machine
Developer laptops and CI runners both. A runner still holding only the old .p12 keeps failing after everything else is fixed.
- 4
Revoke the old certificate last, and only when nothing needs it
If the old key leaked, revoke immediately and accept the disruption. Otherwise let it expire on its own and skip the revocation entirely.
Keep the private key in mind throughout. A certificate is useless without the key that matches it, and a new certificate issued from a new certificate signing request has a new key. If you generate the CSR on one Mac and then try to sign on another without exporting the key, you get a different failure entirely.
Checking your work
# The new identity should be listed and not marked expired security find-identity -v -p codesigning # Does the freshly downloaded profile actually contain the new certificate? security cms -D -i MyApp_AppStore.mobileprovision \ | plutil -extract DeveloperCertificates.0 raw - \ | base64 -D \ | openssl x509 -inform DER -noout -subject -dates
That second command is the one that catches a half-finished rotation. A profile downloaded before the new certificate existed still lists only the old one, and it will look perfectly healthy right up until the build fails.
Related messages
- After rotating, the most common follow-up is provisioning profile doesn't include signing certificate, which means step two above was skipped.
- If the certificate is valid but the key is absent, see missing private key for signing certificate.
- If nothing matching the type is found at all, see no signing certificate found.
- For the full lifecycle, including what expires when, see the guide on Apple certificate expiration.