The problem "code signing" solves
When your Mac downloads an app, how does it know the code came from who it claims to? Apple's answer is a chain of cryptographic proofs — each link proving one narrow claim, and every launch of the app on a user's device re-verifying the whole chain. If any link is broken, modified, or expired, the OS refuses to run the code.
From the outside, "code signing" looks like one thing. Inside, it's four things stacked.
The four layers, from bottom to top
Apple Root CA. A self-signed root certificate Apple owns. Baked into every Mac and iPhone at the factory. The anchor of trust.
Intermediate CA (WWDR). A certificate the root signed, used to sign developer certs. This indirection means Apple can rotate intermediate certs without replacing the root on millions of devices.
Your Apple Distribution certificate. A certificate Apple's WWDR signed for your team. It contains your public key; you hold the matching private key in your login keychain. This is what "signing with your cert" really means — you're signing a hash of your binary with the private key, and the cert is the public half that lets Apple (and users' devices) verify you did.
Your provisioning profile. A bundle that declares three things: which certificate(s) are allowed to sign builds for a given bundle ID, which entitlements the app is allowed to claim (iCloud, push, app groups, etc.), and (for development builds only) which devices are whitelisted. For App Store distribution, Xcode creates and refreshes these automatically; you rarely touch them directly.
Your signed .xcarchive / .app / .ipa. The build, with an embedded signature that chains all the way back up to the Apple Root CA.
What each layer proves
- Cert chain → "This private key belongs to Team ID
HPTTZS5J27, and Apple says that team is real." - Provisioning profile → "The holder of that key is authorized to sign builds for this specific bundle ID with this specific set of entitlements."
- Bundle signature → "Every byte of this binary was blessed by that key at build time — if anything's changed since, the signature won't verify."
You can see why mismatched pieces fail so loudly. A cert for Team A can't sign for a profile scoped to Team B. A profile declaring App Sandbox can't ship a binary missing that entitlement. A binary modified after signing (even by a single byte) fails every verification.
Why Apple stacks it this way
Why not a single signing step, like Android uses? Two reasons:
- Revocation granularity. If a developer's private key leaks, Apple can revoke just that certificate without invalidating every app that team ever shipped. Apps signed before the revocation stay valid; new signatures with the revoked cert fail.
- Entitlement enforcement. Apple can decide which apps get access to camera, location, network extensions, etc. by controlling the provisioning profile issuance. Bundling the list of allowed entitlements into a signed profile means the OS can refuse to honor an entitlement an app declared but wasn't authorized to use.
How xcodebuild ties it together
When LingCode runs xcodebuild archive, it:
- Compiles your Swift / Obj-C into a binary.
- Finds an Apple Distribution certificate in the login keychain matching the team ID declared in your project.
- Asks Apple's developer portal (via Xcode's automatic signing) for a matching provisioning profile — creating or refreshing one as needed.
- Computes SHA-256 hashes of every file in the .app bundle and signs them with the cert's private key.
- Embeds the provisioning profile inside the bundle so the device can verify it at launch time.
- Wraps everything in an
.xcarchivealong withdSYMs and metadata.
Then xcodebuild -exportArchive re-signs for distribution (different method, different destination — App Store vs. Developer ID vs. Enterprise) and produces the uploadable artifact.
The takeaways
- Signing isn't one cryptographic operation — it's a chain of proofs, each answering a different narrow question.
- The certificate proves team identity. The profile proves app authorization. The bundle signature proves integrity.
- Errors are almost always about a mismatch between layers: cert for wrong team, profile for wrong bundle ID, entitlement profile doesn't list, etc. LingCode's App Store guide maps each error to the specific layer to check.
- Apple's model is strict because it has to be: the trust chain on your Mac is the only thing between users and malware masquerading as your app.