Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

iOS Code Signing: Part 4, Provisioning Profiles

DZone's Guide to

iOS Code Signing: Part 4, Provisioning Profiles

In Part 4 of this iOS code signing tutorial series, we will cover the iOS-specific concepts of provisioning profiles and entitlements.

· Mobile Zone ·
Free Resource

This is Part 4 of the iOS Code Signing tutorial series. This series covers fundamentals of the iOS code signing process. You can find Part 1 here.

In the previous post, we looked into certificates and private keys required for code signing iOS apps. Now we will cover some important concepts specific to iOS: provisioning profiles and entitlements. Historically, provisioning profiles are considered the most painful things in the iOS code signing process. Before we go into the details of provisioning profiles, let's understand some terminologies and concepts that we need to know to understand the provisioning profile. The things we must know before we understand provisioning profiles are

  • Team ID
  • Bundle ID
  • App ID
  • Device ID
  • Entitlements

We will see each of this one by one before diving into the provisioning profile.

Team ID

The team ID is a unique identifier for each development team. You can find the team ID in the Apple Developer portal, in the membership section.

You can also find the team ID from your development or distribution certification in the keychain.

$ security find-identity -v -p codesigning

You will see the output which has teamID inside it. It's in the form of "iPhone Developer: Team Name (YOUR_TEAM_ID)." The team ID value is sensitive so you have to keep it secret, and we need that in iOS code signing.

Bundle Identifier

Every iOS app requires a unique identifier so that app can be identified without ambiguity. Every iOS app must set a unique bundle identifier in the reverse domain name of the company or individual. The bundle ID can be set up while creating an app in iTunes Connect, which needs to be used in the property list file of the iOS apps. Each target in the iOS app should have a unique identifier. You might have seen the bundle identifier in the format or something like this. In Xcode, we can see the Bundle Identifier in the General tab of the target.

We can also find the bundle identifier of the apps by looking at the PRODUCT_BUNDLE_IDENTIFIER in the target Build Settings.

App Identifier

Generally, the App ID is the combination of team ID and the bundle ID. It's very common these days that a team can develop more than one app with different bundle identifiers. The App ID can be used to identify one or more apps. The Team ID is supplied by Apple, while the bundle ID search string is supplied by you to match either the bundle ID of a single app or a set of bundle IDs for a group of your apps. The App ID has two types of single App ID for single apps and the Wildcard App ID for a set of apps. You can read more about the App ID on the Apple official documentation here.

Device ID

Every iOS has a unique identifier also called UDID. With UDIDs, each iOS device can be found uniquely. Basically, UDID is 40 characters long number made up of the combination of numbers and letters. Like devices, each simulator also has unique UDID. We can find device UDID by attaching the device to Mac and with iTunes connect. There is a great guide here to find out device UDID using iTunes. You can simply get the UDID of the all attached devices and simulators using the following command:

$ instruments -s devices

You can see the devices with their name, iOS versions, and UDID in the square brackets. It looks something like this:

iPhone 6 Plus (11.2) [22B35E0D-C505-4713-8126-80D39A15B34C] (Simulator)

You can see the device UDID 22B35E0D-C505-4713-8126-80D39A15B34C in the brackets for the iPhone 6 Plus simulator. In the same way, you can get it for the attached real devices.

Entitlements and App Sandbox

iOS apps can't do everything on their own; we have to explicitly tell the app what app can do or can not do in the form of entitlements. The app is entitled to do certain things that we need to define in the app sandbox. The app restrictions are managed by the sandbox. The app sandbox is a different infrastructure from the code signing infrastructure; code signing is responsible for running what's inside the sandbox. The app entitlements are configured to specify which resources of the system that app is allowed to use and under what situation. Entitlements also confer capabilities and security of an iOS app. The resources that the app is allowed to use usually have some default values, but they are always disabled. The app developers have to enable them explicitly. The entitlements values that are commonly used are iCloud, push notifications, Apple Pay, App Sandbox, and more. We can enable the entitlements in the Xcode capabilities tab for a specific target. You should only enable those entitlements that you need. This prevents malicious code from entering the app. There are some restrictions on entitlements that you only use for debug builds, e.g. get-task-allow is only allowed to run an app that is signed by a development certificate. This entitlement is not allowed for an app signed with a distribution certificate.

You can read more about entitlements in Apple's official documentation here and about the app sandboxing infrastructure here get a deep understanding.

Now that we see that what entitlements are, let's see how to check an app's entitlements and internal representation of the entitlements. Basically, entitlements are the property list, a.k.a. plist file generated by Xcode when you enable some capabilities. Xcode will generate the .entitlements file depending on what capabilities you selected. A typical file looks like this:

<?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>aps-environment</key>
	<string>development</string>
	<key>com.apple.developer.icloud-container-identifiers</key>
	<array/>
	<key>com.apple.developer.ubiquity-kvstore-identifier</key>
	<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
</dict>
</plist>

This file shows that we have iCloud and push notifications enabled. There is also a build setting,  Code Signing Entitlements, which takes these entitlements while building an app.

In the case of an already compiled app, you can inspect the app entitlements using the codesign command line tool:

$ codesign -d --entitlements :- /Users/shashi/Library/Developer/Xcode/DerivedData/iOSCodeSigning-gakpslthucwluoakkipsycrwhnze/Build/Products/Debug-iphones/iOSCodeSigning.app

This will list all the entitlements of your pre-compiled app. In case you want to check the entitlements of the .ipa file, you need to follow these steps:

  1. Find the .ipa file and change its the extension to .zip.
  2. Expand the file .zip. This will produce a payload folder containing your .app bundle.
  3. Use the codesign tool to check the entitlements on the .app bundle, as mentioned below.

In the latest version of Xcode, if you archive an app and export a file, then in the Xcode organizer window, you will see all the details with entitlements of the app in a summary tab.

Provisioning Profiles

At this point, you must be wondering why we need to know all the above things in order to know provisioning profiles. The short answer is provisioning profiles are the combination of all the things that we have seen so far. Basically, it consists of everything from certificates, app ID, device ID and entitlements. The provisioning profiles define the rule for running the app inside the device. Its role is to confirm that

  • It is a specific app with an App ID.
  • It is an app with an App ID that can run on certain devices included in the provisioning profile. The development provisioning profiles have the list of devices included whilst distribution provisioning profiles don't have the list of devices.
  • The app only has those entitlements defined in the provisioning profile.
  • The app can only run trust based on the certificate embedded in the provisioning profile.

Creating the Provisioning Profile

There is a lot in the provisioning profile. It has the App ID, device UDIDs, entitlements, and certificate. We will understand this better when we create provisioning profile from the Apple developer portal. The profile can be easily created from the Certificates and Profile tab of the developer portal. Let's add the development profile for the demo app:

Note that we are creating the profile for development purposes. In the next step, the portal will ask you which app you want to create the profile for. We need to select the App ID; in this case, we are using the demo iOSCodeSigning app.

As we are creating the profile for development, we need to select the certificate for the profile as "development certificate."

Note that we have provided the information about the App ID and certificate for creating the profile. In the step, we need to specify devices for this profile.

Finally, we have to give a proper name to profile and it will be ready to download.

Once you have clicked on the Download button, it will be downloaded to the directory. Once you have double-clicked on the profile, it will be installed on the local machine. Now we have the profile downloaded locally, which also has the entitlement that we already enabled from Xcode. You can see those entitlements from the enabled services in the Apple developer portal.

As mentioned earlier, we have provided all the details like App ID, Certificate, Devices, and entitlements to generate the provisioning profile. Now you should understand why we need to know more about these things before learning about the provisioning profiles.

Inside the Provisioning Profile

The provisioning profile is at the core of the code signing that binds signing, entitlements, and the app sandbox together. The profiles can be used to enable the app for development, ad-hoc, enterprise, and app store distribution. It's fairly easy to generate multiple profiles for the same certificate, which adds complexity in the process of code signing, to figure out which profile has been used for signing the app. Since Xcode 8, Apple introduced automatic code signing, which handles profiles automatically and makes the process easier for development, but automating the release process from Continuous Integration became harder. It's important to understand what the provisioning profile is and how it's been used for signing apps.

Once we download and install the provisioning profile, the profiles are stored in the following directory on the local machine:

~/Library/MobileDevices/Provisioning Profiles

We can see all the provisioning profiles inside that directory. The profiles have the .mobileprovision extension, but how do we open this file in order to see the contents of the profile? It's not the property list file - it's stored in the Cryptographic Message Syntax format. Apple uses this format to ensure that the profile isn't changed once it's signed and issued by Apple. We can use the security command line tool to explore the contents of the profile. Let's navigate to the profiles directory and look at the contents of the provisioning profile:

$ cd ~/Library/MobileDevice/Provisioning\ Profiles/
$ security cms -D -i xxxxxxxx_your_pp_id.mobileprovision

This will give you all the information about the provisioning profile in a property list format. The property list file contains some main keys, including

  • App ID Name: This is the App ID of the iOS application.
  • Creation Date: This is the date when the profile was created.
  • Platform: This is usually iOS for iOS apps.
  • Developer Certificates: This is an array of the certificates used to sign the profile.
  • Entitlements: This is a dictionary of the array containing all the entitlements of the app.
  • Expiration Date: Expiry date of the provisioning profile.
  • Name: The name of the provisioning profile.
  • Provisioned Devices: This is an array of device UDIDs of the devices registered with the profile.
  • Team Identifier: This is the team ID of the organization or development.
  • Team Name: This is the real name of the team, not the ID.
  • Version: This is the version of the profile, usually 1 if a profile isn't updated.

As you can see, provisioning profiles have all these details to sign our apps. If anything changes, we need to modify the profile accordingly.

Xcode and Provisioning Profiles

Since Xcode 8, Apple has completely refactored the code signing infrastructure by introducing automatic signing. The WWDC 2016 video here is must watch to understand the changes in code signing. Xcode now creates profiles locally, which handles the signing by adding another build setting: PROVISIONING_PROFILE_SPECIFIER. Using the specifier, we have to specify the provisioning profile by name along with the build setting DEVELOPMENT_TEAM. If the profile name becomes invalid or missing, then it causes a build failure. By using the PROVISIONING_PROFILE, it uses the UDID of the provisioning profile, rather than the name.

Xcode has now deprecated the option to provide an option to specify the provisioning profile in automated signing.

In manual signing, we can still specify the provisioning profile in the build setting.

At this stage, we have covered most of the things about the provisioning profile. In the next post, we will see the actual code signing process for iOS apps.

Stay tuned for Part 5!

Topics:
mobile ,mobile app development ,ios ,code signing

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}