Apple

How iOS Code Signing Works: Certificates, Provisioning Profiles & Private Keys

Koen Van Looveren

12/6/2025

A visual representation on how signing works.

When you start developing for iOS, you quickly bump into Apple’s code signing system. Signing certificates, provisioning profiles, entitlements, private keys — it can feel like a confusing black box.

TL;DR Summary

  • You create a private key on your mac.
  • Apple gives you a certificate that links your identity to your key.
  • A provisioning profile defines where and how your app can run.
  • iOS checks everything before allowing the app to install and execute.

Why Does iOS Require Code Signing?

Apple uses code signing to:

  • Verify that an app comes from a known, trusted developer.
  • Ensure that the app hasn’t been tampered with after signing.
  • Control which apps can be installed on iOS devices (for security & control of the App Store ecosystem).
  • Restrict certain features to authorized developers (like Push Notifications, HealthKit, etc).

No signed app → no install.

The Main Players

Here are the key components involved in signing an iOS app:

Component Description
Apple Developer Account Your registered identity as a developer with Apple.
Certificate Signing Request (CSR) A file you generate to request a certificate from Apple. Contains your public key.
Private Key A secret key you generate locally and store in your Keychain.
Signing Certificate Issued by Apple. Ties your public key to your developer identity.
Provisioning Profile A file that links your certificate, app ID, entitlements, and target devices.
Entitlements Special permissions your app requests (like Push, iCloud, App Groups, etc).

The Flow: Step-by-Step

Let’s walk through the signing process:

1️⃣ You Generate a Key Pair

  • On your Mac, you create a private/public key pair.
  • The private key stays on your machine (in your Keychain).
  • The public key is included in a Certificate Signing Request (CSR).

2️⃣ Apple Issues a Signing Certificate

  • You upload the CSR to Apple via the Developer Portal (or Xcode does it for you).
  • Apple verifies your Developer Account and issues a Signing Certificate.
  • The certificate contains:
    • Your public key.
    • Your identity (developer or team).
    • Apple’s digital signature (proving it’s a valid Apple-issued cert).

3️⃣ You Build Your App

When you compile your app:

  • Xcode uses your private key to sign the app bundle.
  • The signature proves that the app comes from you, and that the code hasn't changed since signing.

4️⃣ You Create a Provisioning Profile

  • A Provisioning Profile combines:
    • The app’s Bundle ID (your App ID).
    • Your Signing Certificate.
    • The device UDIDs (for development profiles).
    • Any enabled entitlements.
  • The profile tells iOS: "This app is allowed to run on this device, and it comes from this authorized developer."

5️⃣ The Profile Is Embedded

  • The provisioning profile is embedded into the app bundle.
  • When the app launches, iOS verifies:
    • The app is signed with the matching private key.
    • The certificate is still valid.
    • The profile allows execution on this device.
    • The entitlements match.

Quick Overview: Types of Certificates

Certificate Type Purpose
Development Certificate For debugging on real devices during development.
Distribution Certificate For TestFlight, App Store, or Enterprise deployment.
Enterprise Certificate For internal corporate apps distributed outside App Store (requires special account).

Quick Overview: Types of Provisioning Profiles

Profile Type Used For
Development Install app on registered devices for testing.
Ad Hoc Share app outside App Store with limited devices.
App Store Submit to App Store or TestFlight.
Enterprise Internal distribution inside your company.

Private Keys: The Weak Link

  • The private key is the most sensitive piece.
  • Without it, you can’t sign new builds.
  • If you lose it, you’ll need to revoke the certificate and create a new one.
  • This is why Apple often warns: "Keep your private keys safe and backed up."

👉 If you're using multiple Macs, make sure you export and import the private key + certificate to avoid signing issues.

Simplified Visual

[ Private Key (your Mac) ] <--signs--> [ App Bundle ]
[ Public Certificate (issued by Apple) ]
[ Provisioning Profile (rules + entitlements) ]       
[ iOS Device: validates everything ]

Modern Bonus: Xcode & Automatic Signing

  • Xcode tries to automate a lot of this.
  • When “Automatically Manage Signing” is enabled, it:
    • Generates CSRs.
    • Creates provisioning profiles.
    • Downloads certificates.
    • Syncs profiles.
  • For many developers, this hides most of the complexity.
  • BUT: when things break (or on CI/CD pipelines), understanding the manual process is invaluable.

Gotchas to Avoid

  • ✅ Always backup your private key.
  • ✅ Revoke old certificates you no longer need.
  • ✅ Watch out for expiring provisioning profiles.
  • ✅ For CI/CD: securely store certificates and private keys.

Setup your perfect CI/CD

In most local development workflows, Xcode handles this for you automatically behind the scenes. But once you move into CI/CD pipelines, automation, fastlane, the impaktfull_cli or other advanced release processes, you almost always need to provide an exportOptions.plist file explicitly to control the export behavior and avoid interactive prompts.

Example for the App Store

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>app-store</string>
    <key>uploadBitcode</key>
    <false/>
    <key>uploadSymbols</key>
    <true/>    
    <key>signingStyle</key>
    <string>manual</string>
    <key>provisioningProfiles</key>
    <dict>
        <key>com.yourcompany.yourapp</key>
        <string>Your_Provisioning_Profile_Name</string>
    </dict>
    <key>teamID</key>
    <string>YOUR_TEAM_ID</string>
</dict>
</plist>

Example for Ad Hoc

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>ad-hoc</string>
    <key>signingStyle</key>
    <string>manual</string>
    <key>provisioningProfiles</key>
    <dict>
        <key>com.yourcompany.yourapp</key>
        <string>Your_Provisioning_Profile_Name</string>
    </dict>
    <key>teamID</key>
    <string>YOUR_TEAM_ID</string>
</dict>
</plist>

How to use the export options?

xcodebuild -exportArchive \
  -archivePath path/to/YourApp.xcarchive \
  -exportOptionsPlist path/to/exportOptions.plist \
  -exportPath path/to/exported/ipa

App Signing at impaktfull

We want to have as much control over our signing flow as possisble. This is why we always choose manual signing for alpha, beta and production. For development it is just easier to test things out quickly so we enable automatic signing.

Sharing your private keys should be done in a secure way. This is why using 1password and the impaktfull_cli to create builds is super important for our CI/CD flow. It allows us to create a new private key that is instantly available on all CI/CD runners.

Conclusion

Apple’s signing system is strict, but for good reason: it ensures security, trust, and app integrity across millions of devices. While confusing at first, once you understand the building blocks, certificates, keys, and profiles, it starts to make sense.

let's talk

Ready for take off?

Rocket Icon