Skip to content

DOMAIN:IOS_DEVELOPMENT:CODE_SIGNING

OWNER: martijn
ALSO_USED_BY: valentin, leon (release), alex/tjitte (CI/CD)
UPDATED: 2026-03-24
SOURCE: Apple Developer Documentation — Code Signing, Provisioning


SIGNING:DEVELOPER_PROGRAM_TYPES

Program Fee Distribution Use Case
Apple Developer Program (Individual) $99/yr App Store, TestFlight, Ad Hoc Solo developer, freelancer
Apple Developer Program (Organization) $99/yr App Store, TestFlight, Ad Hoc, Custom Apps, Unlisted Business entity (BV, GmbH, Ltd)
Apple Developer Enterprise Program $299/yr In-House only Internal apps for 100+ employee orgs
Free (Personal Team) $0 Xcode direct install only Development/learning only

RULE: free tier = 7-day signing, 3 app limit, no distribution, no push notifications, no CloudKit
RULE: Organization account requires DUNS number + Apple enrollment review
RULE: Enterprise requires DUNS + 100+ employees + Apple manual review (stricter)


SIGNING:CERTIFICATE_TYPES

Certificate Purpose Created In Max Active Validity
Apple Development Sign app for development/debug Xcode or Developer Portal Unlimited 1 year
Apple Distribution Sign app for App Store/TestFlight Xcode or Developer Portal 3 1 year
Apple Push Notification (APNs) Push notifications Developer Portal 2 (sandbox + production) 1 year
In-House Distribution Enterprise distribution only Developer Portal 3 3 years
Pass Type ID Apple Wallet passes Developer Portal per pass type 1 year

Certificate Chain

Apple Root CA
└── Apple Worldwide Developer Relations CA
    ├── Apple Development: {Team Name} ({Team ID})
    ├── Apple Distribution: {Team Name} ({Team ID})
    └── Apple Push Services: {Bundle ID}

RULE: certificates are team-wide, not per-app
RULE: IF certificate expires THEN new builds cannot be signed, but existing installed apps continue working
RULE: IF certificate is REVOKED THEN enterprise apps stop working immediately

Creating Certificates

TOOL: Xcode → Settings → Accounts → Manage Certificates → + (recommended)
TOOL: manual: create CSR in Keychain Access → upload to Developer Portal → download .cer

RUN: security find-identity -v -p codesigning — list all valid signing identities in keychain

ANTI_PATTERN: creating certificates on different machines without exporting
FIX: export .p12 from Keychain Access (cert + private key) and share securely with team
FIX: better: use Fastlane match (see CI/CD section below)


SIGNING:PROVISIONING_PROFILES

Profile Type Signs With Devices Purpose
iOS Development Development cert Registered devices Run from Xcode on test devices
Ad Hoc Distribution cert Up to 100 registered Test distribution builds
App Store Distribution cert ANY (Apple validates) App Store + TestFlight
Enterprise (In-House) In-House cert ANY in organization Internal distribution

Profile Contents

A provisioning profile bundles:
1. App ID (bundle identifier, explicit or wildcard)
2. Certificate(s) (which signing identities can use this profile)
3. Device UDIDs (development and ad hoc only)
4. Entitlements (capabilities the app is allowed to use)
5. Expiration date (1 year, or 3 years for enterprise cert-based)

RULE: profiles expire annually — set calendar reminder 30 days before
RULE: expired profile = cannot install, cannot build for that distribution type
RULE: App Store profiles do NOT include device UDIDs — Apple validates at install time

Managing Profiles

TOOL: Xcode → Signing & Capabilities → Automatic signing (recommended for development)
TOOL: Developer Portal → Profiles → create/download/renew
TOOL: fastlane sigh (download/create profiles programmatically)

RUN: security cms -D -i /path/to/profile.mobileprovision — inspect profile contents
RUN: open /path/to/profile.mobileprovision — view in Finder
RUN: find ~/Library/MobileDevice/Provisioning\ Profiles/ — all installed profiles


SIGNING:ENTITLEMENTS_AND_CAPABILITIES

Entitlements declare what OS features an app can use. Configured in:
1. App ID in Developer Portal (registers capability)
2. Xcode → Target → Signing & Capabilities (configures entitlement)
3. .entitlements file in project (embedded in binary)

Common Entitlements

Entitlement Key Requires
Push Notifications aps-environment APNs cert, App ID config
iCloud com.apple.developer.icloud-container-identifiers iCloud container
App Groups com.apple.security.application-groups Group identifier
Associated Domains com.apple.developer.associated-domains AASA file on server
HealthKit com.apple.developer.healthkit Privacy description
Sign in with Apple com.apple.developer.applesignin Service ID config
Apple Pay com.apple.developer.in-app-payments Merchant ID
Background Modes UIBackgroundModes Info.plist declaration
Keychain Sharing keychain-access-groups Group identifier
Network Extensions com.apple.developer.networking.networkextension Apple approval

RULE: entitlements in app binary MUST match entitlements in provisioning profile
RULE: IF mismatch THEN build succeeds but install/launch fails with cryptic error
RULE: some entitlements require explicit Apple approval (network extensions, CarPlay, DriverKit)

ANTI_PATTERN: adding entitlement in Xcode but forgetting to enable in Developer Portal
FIX: Xcode automatic signing handles this — IF using manual signing, update both places

ANTI_PATTERN: requesting entitlements the app doesn't actually use
FIX: Apple reviews entitlement usage — unused entitlements cause rejection


SIGNING:XCODE_MANAGED_VS_MANUAL

IF: single developer or small team
THEN: use Xcode automatic signing
- Xcode creates/manages development certificates
- Xcode creates/manages provisioning profiles
- Xcode registers new devices automatically
- Less control but fewer signing errors

Manual Signing (Required for CI/CD)

IF: CI/CD pipeline or team of >3 developers
THEN: use manual signing with Fastlane match
- Full control over certificates and profiles
- Reproducible across machines
- Required for most CI/CD setups


SIGNING:CI_CD_SIGNING

STANDARD: Fastlane match documentation
CONCEPT: store encrypted certificates + profiles in a private Git repo or cloud storage
ALL team members and CI machines use the same signing identity

# Matchfile
git_url("https://github.com/client-org/ios-certificates.git")
storage_mode("git")
type("appstore") # or development, adhoc
app_identifier("com.client.appname")
team_id("TEAM_ID_HERE")

TOOL: fastlane match development — create/fetch development cert + profile
TOOL: fastlane match appstore — create/fetch App Store distribution cert + profile
TOOL: fastlane match adhoc — create/fetch Ad Hoc cert + profile
TOOL: fastlane match nuke distribution — revoke ALL distribution certs (DANGEROUS)

RULE: match password MUST be stored in CI secrets (not in repo)
RULE: match repo MUST be private
RULE: IF switching to match THEN nuke existing certs first (one-time migration)

Xcode Cloud

IF: using Apple's native CI/CD
THEN: signing is managed automatically
- Xcode Cloud has access to Apple Developer account
- No need for manual cert management
- Configure in Xcode → Product → Xcode Cloud

GitHub Actions / Other CI

# Example: install certificate and profile on CI runner
- name: Install signing certificate
  env:
    CERTIFICATE_BASE64: ${{ secrets.P12_BASE64 }}
    CERTIFICATE_PASSWORD: ${{ secrets.P12_PASSWORD }}
  run: |
    CERT_PATH=$RUNNER_TEMP/certificate.p12
    KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
    echo -n "$CERTIFICATE_BASE64" | base64 --decode -o $CERT_PATH
    security create-keychain -p "" $KEYCHAIN_PATH
    security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
    security unlock-keychain -p "" $KEYCHAIN_PATH
    security import $CERT_PATH -P "$CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
    security list-keychain -d user -s $KEYCHAIN_PATH

ANTI_PATTERN: storing .p12 files in the code repository
FIX: use CI secrets, Fastlane match, or 1Password CLI for cert distribution

ANTI_PATTERN: using a developer's personal certificate in CI
FIX: create dedicated CI certificate or use Fastlane match


SIGNING:COMMON_ERRORS

"No signing certificate found"

CHECK: is the certificate installed in the keychain?
CHECK: is the private key present? (certificate without key = useless)
CHECK: has the certificate expired?
RUN: security find-identity -v -p codesigning — verify
FIX: re-download from portal, or re-export .p12 from another machine

"Provisioning profile doesn't include signing certificate"

CHECK: the profile was created with a different certificate
FIX: regenerate profile in Developer Portal with current certificate
FIX: or download correct certificate that matches profile

"A valid provisioning profile matching the application's Identifier could not be found"

CHECK: Bundle Identifier in Xcode matches App ID in Developer Portal
CHECK: profile is installed (~/Library/MobileDevice/Provisioning Profiles/)
CHECK: profile is not expired
FIX: Xcode → Signing & Capabilities → refresh, or re-download from portal

"The executable was signed with invalid entitlements"

CHECK: entitlements in .entitlements file match profile's allowed entitlements
CHECK: capability enabled in Developer Portal AND in Xcode
FIX: regenerate provisioning profile after enabling capability in portal

"This app could not be installed at this time" (device install failure)

CHECK: device UDID is in the provisioning profile (Ad Hoc/Development)
CHECK: profile is not expired
CHECK: iOS version on device is >= deployment target
FIX: add device UDID to portal → regenerate profile → re-sign build


SIGNING:YEARLY_RENEWAL_CHECKLIST

ANNUAL (at Apple Developer account renewal):
□ Renew Apple Developer membership (auto-renew or manual)
□ Regenerate expiring distribution certificates
□ Regenerate all provisioning profiles
□ Update Fastlane match repo with new certs/profiles
□ Test CI/CD pipeline with new signing identity
□ Update push notification certificates (APNs)
□ Verify all production apps still downloadable
□ IF enterprise cert renewal THEN redistribute all in-house apps

30 DAYS BEFORE EXPIRY:
□ Calendar alert for certificate expiration
□ Calendar alert for profile expiration
□ Calendar alert for push cert expiration
□ Pre-generate new certs in test environment