A summary-ish of things related to code signing, the developer portal, and provisioning profiles.
I believe this is mostly accurate - but I admit that I have not dug deep into the internals because this rough understanding has served me well enough so far. Don’t take this as some kind of technical gospel to swear by. Instead this is meant more to help anyone who might be really confused to start to find meaning in what can seem like chaos and pointless complexity. Putting this together helped me clarify the reasons for the different parts and roughly how they fit together and why they’re necessary. If you want to know precise technical details about this stuff, I’m not your guy - I just want to build apps and not spend all my time fighting the technology. I hope this helps someone find peace without adding too much confusion.
* An “App ID” represents a class of capabilities (entitlements) for things that require an “account” or “identity” or “permission” of sorts on Apple’s servers and/or on device. It is used when generating a provisioning profile to configure the profile to allow iOS to grant an app permissions to certain capabilities.
* A wildcard App ID is like an “abstract class” which allows the same “identity” for Apple’s services (like GameCenter, Push, IAP, iCloud, etc) to be shared by all apps that implement the “abstract class”. Implementation is done simply be using a bundle ID for your app that matches the App ID’s pattern. (This is probably not a perfect understanding, but that seems like the intent. It can behave a bit like a superclass for sets of entitlements and data containers. While I’ve never used this, I believe you can even share a single iCloud storage container across multiple apps this way.)
* The entitlements *file* that’s part of an app’s bundle acts as *configuration* for certain entitlements or even opting-out of some entitlements that the specific app doesn’t want but which may have been enabled by the provisioning profile that was made with an App ID that matched the app’s bundle identifier when it was launched. It does not itself grant you anything just by virtue of it existing! (Otherwise that wouldn’t be very secure and Apple wouldn’t have much control!)
* A certificate uniquely identifies a developer (individual or company). It is used to check that a given code signature is valid.
* A device, as far as we’re concerned here, is just a unique identifier that represents a single individual piece of hardware.
* Adding an app to iTunes Connect is how you tell the store itself about a specific app and gives the *store* permission to communicate with your app for IAP (as opposed to entitlements specified by the matching App ID which just gives your app permission to talk *to* the store using the builtin frameworks but does not promise your app will get a useful reply from the store). To use and test IAP, you must add your app using your your app’s bundle ID to iTunes Connect and add IAP products (not necessary to submit the app or products for review) or else the store will ignore your app when it tries to use it’s IAP entitlements to talk to the store itself. Think of adding things to iTunes Connect as granting the App Store specific permissions that can affect the behavior of your app (by it offering products for sale, etc).
* Provisioning profiles are where the vasty majority of the confusion is. They represent a union of almost everything mentioned so far and act as a single solution that addresses several issues. The important thing to remember is that they exist to grant an iOS device permission to grant your app specific permissions.
Code signing requires specifying a certificate. That certificate is usually your personal development certificate or your main distribution certificate depending if you’re building for development or getting ready to submit to the store. The code signing process searches your keychain for a private key that matches the specified certificate and uses that private key to generate the signature. (Both the public and private keys are created and stored in your keychain when you initiate the CSR in Keychain Access when you are first setting up your certificates in the developer portal, but only the public key is signed by Apple and turned into your certificate.) Signing marks the binary in such a way that it can be shown that it has not been tampered with since it was signed and that it was signed specifically by you. Any tampering will invalidate the signature - but that only matters if something actually checks and requires the signature to be valid in the first place! Without access to the public key necessary to validate the signature, your binary’s integrity cannot be determined. Having a signed binary alone doesn’t confer it any special privileges.
So how does iOS know that your app is really yours and that you are approved by Apple and that the app has not been tampered with? That’s one of the things that a provisioning profile solves by including exactly which certificates are allowed to be used to sign apps matching a specific App ID. When the provisioning profile is created, the selected certificates are encoded right within the provisioning profile itself so that iOS can use the public keys from those certificates to ensure an app’s binary was signed by one of them before deciding to grant that binary access to anything. This is why provisioning profiles need to have certificates added to them when they are configured.
Another thing that provisioning profiles do is restrict the set of devices allowed to run a given app. This is, in a way, like a second “signature” for the hardware itself. Not only does the binary need to be signed by an approved certificate included in the profile, but the device itself must have a specific “signature” in the form of it’s hardware ID and that ID must also be listed in the provisioning profile before the app is allowed to run.
Provisioning profiles grant access to an app to certain capabilities that Apple wants to control - these are the entitlements that are specified for a given App ID in the portal. The entitlements themselves are encoded within the provisioning profile when it is generated so that iOS knows which things to allow or deny when the app is launched.
Also encoded within the provisioning profile is the distinction between Distribution and Development. This distinction determines which backends some of Apple’s cloud services will connect to if the app has permission to use them (such as sandbox mode for the store or game center) and the certificates used to validate push notifications in either context (which enables you to have separate development and production push notification services and behaviors if you want).
The reason all of this works and is secure is that Apple generates the provisioning profiles in the portal and then signs them with their own private keys before delivering them to you. The signing of the provisioning profiles is something only Apple can do. The file you download can therefore not be tampered with without rendering it invalid. An invalid provisioning profile will not be accepted by iOS and thus Apple can control exactly what can and cannot be provisioned by a developer by simply restricting access to the signing of the provisioning profile to things the the developer portal gives you permission to configure in the first place - even though provisioning profiles can support any number of other awesome options you can’t use without jailbreaking. This is why you have to register testing devices in the portal, add your certificates to the portal, etc - only things in the portal (and thus the numbers of which can be controlled and limited arbitrary by Apple) can be included in a generated and properly signed provisioning profile. The portal is where Apple’s provisioning policies and limitations are actually enforced.
In Xcode, the setting for provisioning profile is listed under Code Signing, but I believe it is not actually referenced in the app bundle nor is it specifically required even when code signing! This may seem surprising, but that’s because the provisioning profile actually has nothing to do with building your app - it’s all about permission when *running* the app on device. That permission is specified from the point of view of the *device*. The device must have a relevant provisioning profile installed for your app and for the device itself in order to run your app’s code, but you do not need one to compile or sign that code! (You only need a public/private key pair to sign your code.) I tested this by simply deleting the provisioning profile from the Xcode’s project settings and cleaning and building and running the project. There was no complaint at all - the app still built was signed and ran on device just fine. Deleting the provisioning profile from the device itself caused the app to stop launching, of course. I think all the provision profile setting in Xcode does is ensure that build & run installs the one you wanted on the device for you automatically and, if there’s any conflicts of found keys in your keychain, it may help to disambiguate which key pair to use when signing (and maybe to aid with the “fix issues” feature).
On device, when attempting to launch an app, iOS will check all installed provisioning profiles and match the app’s bundle ID against the App ID of an installed provisioning profile. It will then use the most-specific one it finds (I think) - but not necessarily the one specified exactly in Xcode’s settings! This can cause problems from time to time if things get out of sync. Basically, the provisioning profile and the app itself are disconnected entirely from each other. You can have any number of apps that use the same provisioning profile (in the case of a wildcard, for example), or you can have any number of provisioning profiles with different combinations of device IDs. It does not need to be one-to-one! This also means you do not need to rebuild your device if you add a tester’s device to your provisioning profile or anything like that. Just get them to install the updated profile and things should be fine.
If iOS finds a relevant provisioning profile and all of the restrictions check out, code signing is validated, device IDs are validated, and the provisioning profile was signed by Apple, then the app is allowed to run. Otherwise you get a provisioning error of some kind and are left with a bunch of combinations of things to check for when attempting to correct it. Fun!
The main takeaway, I think, is that provisioning profiles grant the device permission to grant a set of other permissions to a particular app. Without a profile signed by Apple (or a jailbreak), iOS won’t grant your app any permissions at all and therefore it won’t launch.
(Check out https://github.com/chockenberry/Provisioning for a handy QuickLook plugin that can inspect provisioning profiles.)