A Beginner’s Guide to Code Signing in iOS Development

Bing Kuo
14 min readFeb 20, 2023

--

Certificates, provisioning profiles, code signing, and related concepts are a common part of an iOS engineer’s daily work, and we often struggle with them. In this post, we will delve into the fundamental concept of code signing, certificates, and provisioning profiles in iOS development.

You might think that Xcode’s automatic manage signing provides everything you need, so why bother learning about signing? While it’s true that automatic signing is very convenient for small teams, as your team grows, Xcode easily generates many unused certificates, and your certificates may even be revoked if you exceed the limit. In contrast, manual signing allows you to choose the desired certificates for different configurations or name them according to different projects. It’s more flexible and easier to manage.

Moreover, automatic signing hides most of the details from developers. Having a comprehensive understanding of these concepts can help you deal with related problems with greater confidence. Let’s begin by outlining the topics we’ll cover:

1. What's Code Signing?
2. Basic Knowledge
a. Public-key cryptography
b. Cryptographic hash function
c. Signature
3. Certificate
a. What's certificate?
b. Types of certificate
c. Components of the certificate
d. Certificate Authority (CA)
f. How does it work?
4. Provisioning profile
a. Types of provisioning profile
b. Components of provisioning profile
5. Code signing process
a. Request a certificate
b. Create provisioning profile
c. Sign the app code
d. Distribution scenario
6. Related reads

What’s Code Signing?

Code signing your app assures users that it’s from a known source and hasn’t been modified since it was last signed. Before your app can integrate app services, be installed on a device, or be submitted to the App Store, it must be signed with a certificate issued by Apple.

from: https://developer.apple.com/support/code-signing/

Briefly, a mechanism to ensure your app is integrity and trustworthy. The general process is as follows:

  1. Development environment: Install Xcode on Mac and set up the related tools.
  2. Enroll in the developer program: Sign up for an account by following the article “Manage and use your Apple ID” and enroll in the developer program by following the article “What You Need To Enroll”.
  3. Create an App ID: The App ID is a unique identifier used to distinguish your app from others. You need to create it on the Apple Developer website and enable capabilities if needed.
  4. Create a certificate: Create development and distribution certificates on the Apple Developer website so that you can sign your app.
  5. Create a provisioning profile: A provisioning profile records which devices can run the app, which certificate can sign the app, what the app’s entitlements are, and so on.
  6. Choose a provisioning profile: You can set up different provisioning profiles for different configurations.
  7. Compile and sign your app: Xcode compiles the code, ensures everything is correct using the provisioning profile, and signs your app.

It’s a relatively complex process. Before delving into it, it’s important to have a basic understanding of cryptography, as it can help you understand what a signing is.

Basic Knowledge

Public-key cryptography

Public-key cryptography, or asymmetric cryptography, is the field of cryptographic systems that use pairs of related keys.

from: https://en.wikipedia.org/wiki/Public-key_cryptography

Figure 1: Simplified public-key cryptography

The public key and private key are a pair of keys where the former can be shared with anyone who wishes to send a plaintext. They will encrypt it using the public key and send the resulting ciphertext, which is difficult to read, to you. Upon receiving the ciphertext, you can decrypt it using the private key to obtain the plaintext. It is important to securely store your private key and prevent others from gaining access to it.

This is called asymmetric cryptography because there are two keys involved. In contrast, symmetric cryptography uses the same key. Common asymmetric cryptography algorithms used in iOS development include RSA and ECDSA.

Cryptographic hash function (CHF)

A cryptographic hash function (CHF) is a hash algorithm (a map of an arbitrary binary string to a binary string with fixed size of n bits) that has special properties desirable for a cryptographic application.

from: https://en.wikipedia.org/wiki/Cryptographic_hash_function

Figure 2. Simplified cryptographic hash function

A hash function takes a message as input and generates a fixed-length text known as a digest. The function produces the same digest every time for the same message. This is referred to as a one-way function because the original message cannot be obtained from the digest. However, you can hash the message again and compare the resulting digest with the original digest to check if they are the same.

Digital Signature

A digital signature is a mathematical scheme for verifying the authenticity of digital messages or documents. A valid digital signature, where the prerequisites are satisfied, gives a recipient very high confidence that the message was created by a known sender (authenticity), and that the message was not altered in transit (integrity).

from: https://en.wikipedia.org/wiki/Digital_signature

Figure 3. Simplified signature process

We can combine the above two technologies to achieve the digital signature. It’s worth noting that, in the digital signature, we use the private key to encrypt and the public key to decrypt, which is the reverse of public-key cryptography. The steps to create and verify a digital signature are:

  1. Hash your message to generate a digest. Hashing your message to generate a digest reduces the time it takes to generate a signature, compared to directly encrypting messages.
  2. Use the private key to encrypt the digest and generate a signature. This action is called signing.
  3. Send the signature, public key, and message to the receiver.
  4. The receiver hashes the message to generate a digest.
  5. The receiver decrypts the signature using the public key to obtain the digest’.
  6. Check whether the digest and digest’ match. If the message is forged by others, it will not match, and vice versa.

In iOS development, the message will be the app code. Xcode signs the code and submit it to the App Store or distribute it to devices. The signature proves that the app is intact. However, others can generate a public key, message, and signature to pretend to be the sender, and step 6 will be verified successfully too. To ensure that the public key is from the correct sender, we use digital certificates. In the next section, we will discuss what’s digital certificates.

Certificate

What’s certificate?

In cryptography, a public key certificate, also known as a digital certificate or identity certificate, is an electronic document used to prove the validity of a public key.

from: https://en.wikipedia.org/wiki/Public_key_certificate

The digital certificate is provided by a CA (Certificate Authority) to prove the owner’s identity. It’s used to check whether the app source is trustworthy and to verify that it has not been tampered with by a malicious actor. For security reasons, certificates have an expiration date to ensure that they are managed, trusted, and to reduce the risk of the certificate being used for malicious purposes. When a certificate expires, it can’t be used for code signing, and a new certificate must be obtained to continue signing code.

Types of certificate

There are two main types of certificates in iOS development: development certificates and distribution certificates.

Development certificates are used during development and testing, allowing you to install and run your device. Distribution certificates used deploy an app, allowing you to submit to AppStore or install to the user’s device.

To know other certificate types, refer to “Certificate types.”

Components of the certificate

There are many formats of certificates. Among them, .cer and .p12 are commonly used in iOS development.

Figure 4. Simplified X.509 certificate

The .cer file is X.509 certificate. You’ll get this type if you download it on the Apple Developer website. It contains:

  1. The owner’s public key is used to decode the signed app to verify the app’s integrity. In this situation, the private key usually saves on the owner’s keychain.
  2. Information, such as email
  3. A digital signature from CA proves this certificate and the public is trustworthy.
Figure 5. Simplified PKCS#12 certificate

The .p12 file is an encoded certificate with PKCS #12 format. It typically contains a private key and a X.509 certificate. You can obtain it by generating a certificate through Xcode or by someone export a certificate from their keychain and share it with you.

Certificate Authority (CA)

CA is an organization that manages certificates. Basically, everyone can be a CA, but choosing a CA which large and under review is safer.

Figure 6. Simplified chain of trust

In iOS development, our certificate called end-entity certificate or leaf certificate is issued by Apple Worldwide Developer Relations Certification Authority (AppleWWDRCA).

The AppleWWDRCA is an intermediate certificate issued by Apple Root CA and is used to verify the signature of apps submitted by developers. It is installed when you install Xcode, or you can download and install it from “apple.com/certificateauthority”.

The Apple Root CA is a root certificate issued by itself. It is installed in your iPhone, Mac, or any Apple device by default, avoiding the possibility of the CA’s public key being forged. See the document “List of available trusted root certificates” for more details.

The above security mechanism is called Chain of trust. Each certificate is signed by its superior certificate until the final certificate on the chain is signed by a trusted root certificate.

How does it work?

Figure 7. Simplified the process of using certificate
  1. We know the signature is signed with the private key. Therefore, we can use the CA’s public key to decrypt it to get the owner’s public key and information.
  2. Compare whether the information obtained from the certificate is consistent with the information obtained from step 1. If it is the same, it means the certificate is from the correct source, and the public key is trustworthy.

Returning to the problem from the end of the previous section, the certificate contains the public key and is authorized by a trusted CA. Therefore, through the certificate, we can ensure the authenticity of the public key. However, there are other issues to consider, such as who is authorized to sign code, where can those apps run, and how can those apps be entitled. The provisioning profile stores those information and is packaged during archiving. We will discuss this in the next section.

Provisioning profile

The profiles resource represents the provisioning profiles that allow you to install apps on your iOS devices or Mac. You can create and delete provisioning profiles, and download them to sign your code.

from: https://developer.apple.com/documentation/appstoreconnectapi/profiles

A Provisioning Profile is a property list that contains a set of specific configuration information that is required to install for development and distribution. It works on the following scenario:

  1. Xcode checks whether the App ID, entitlements, and certificates match the provisioning profile. If there is any mismatch, it will show errors, and you won’t be able to compile it.
  2. When archiving, Xcode packages the provisioning profile into a .ipa file. The iOS device will check whether the app’s configuration matches the provisioning profile.
  3. When you submit an app to the App Store, the App Store will check whether the app is signed and provisioned correctly.

Types of provisioning profile

Figure 8. Simplified development provisioning profile

The development provisioning profile allows you to install development apps on test devices. It contains a device list that records which devices can run the app. As well as it includes multiple development certificates, which means you can develop on different Macs or by other users.

That’s why you need to enable the certificate or device when you have a new of them and re-download the profile (in most cases, Xcode will update automatically).

Figure 9. Simplified distribution provisioning profile

Distribution provisioning profile allows you to distribute the app to App Store or device. There are several types:

  • Ad hoc distribution profiles allow you to install on test devices. It’s very similar to development profiles, but the certificate type is different, closer to store builds, and has more security. For more discussion, see “Difference ios ipa export with development or ad hoc profile.”
  • App Store distribution profiles allow you to submit to App Store, which only chooses a distribution certificate. If you open the property list, you will see it doesn’t have ProvisionedDevices property, so you can’t run an App Store distribution signed app locally.
  • In-House (Enterprise) distribution profiles allow you to install to any device without being in the device list. It has ProvisionsAllDevices so that you can run the app on all devices. However, to have the capability to select this option, you must pay more money to have an enterprise account.

Components of provisioning profile

The provisioning profile files are wrapped within a Cryptographic Message Syntax (CMS) signature. You can run the following command to view the original property list:

security cms -D -i <profile_name>.mobileprovision -o <output_name>.plist

The property list contains four main pieces of information: App ID, Entitlement, Device ID, and DeveloperCertificates.

App ID (application identifier) is composed of two strings: Team ID and Bundle ID. Depending on your requirement, it could be an explicit App ID (teamID.com.domain.appName) or a wildcard App ID (teamID.*). For more information on the topic of explicit v.s. wildcard, see the article “When should I use a wildcard App ID vs. an explicit App ID?”.

<key>Entitlements</key>
<dict>
<key>application-identifier</key>
<string>teamID.com.domain.appName</string>
</dict>

Entitlements confer specific capabilities or security permissions to your iOS or macOS app, such as Apple Pay and iCloud storage. Xcode will create when you choose one of the capabilities for the first time. Before you add capabilities to your Xcode, you need to enable them on the App ID on the Apple Developer website. Otherwise, Xcode will show errors to notice you both are not matched.

<key>Entitlements</key>
<dict>
<key>aps-environment</key>
<string>development</string>

<key>com.apple.developer.in-app-payments</key>
<array>
<string>merchant.com.domain.appName</string>
</array>

<key>com.apple.developer.associated-domains</key>
<string>*</string>
</dict>

Device ID is a list of UDID (unique device identifiers) that shows which device can run the app.

# development profile
<key>ProvisionedDevices</key>
<array>
<string>00000000-0000000000000000</string>
</array>

# in-house profile
<key>ProvisionsAllDevices</key>

DeveloperCertificates is a list of certificates showing who holds the certificate developer can sign the code. It’s based on Base64 encoded and in PEM format (Privacy-Enhanced Mail, a standard certificate format, RFC-7468). You have two ways to see more information.

<key>DeveloperCertificates</key>
<array>
<data>MIIFxDCCBKygAwIB ... h/+mlX1oAOH6CtOLykA==</data>
</array>

The first way is using OpenSSL. Copy the encoded text between <data></data> and paste to new file, like this:

-----BEGIN CERTIFICATE-----
MIIFwzCCBKugAwIBAgIQGYAb0U7jx...
-----END CERTIFICATE-----

Save to a .pem file. After that, run the following command:

openssl x509 -text -in <file_name>.pem

The second way is using certtool. To extract a specific certificate, add its index to the key path:

# extract the first certificate and save it to cert0.cer file
plutil -extract DeveloperCertificates.0 raw -o - <output_name>.plist | base64 -D > cert0.cer

# print information
certtool d cert0.cer

Code signing process

Figure 10. Code signing process in iOS development

Figure 10 shows the code signing process in development. I separate them into 3 parts to introduce each part step by step.

Request a certificate

Step 1.
Create a CSR (certificate signing request) from Keychain Access. You can imagine it’s a form that applies for a certificate with Apple. When you create the CSR file, at the same time, Keychain Access generates a key pair (public/private key) in your keychain. And the public key and your information will save a CertificateSigningRequest.certSigningRequest file. You need to save it on your Mac. For more detailed creation steps, you can see “Create a certificate signing request.

Step 2.
Go to the Apple Developer website. Click the create button on the certificate page and choose the development type. Apple will ask you to upload the CSR file in the process.

Step 3.
Apple verify the file’s correctness you provide.

Step 4.
To ensure this file don’t being forged by others, Apple signs this file with CA’s private key and combines this file and signature into a certificate. However, in the real world, there are other ways that you can get a certificate:

From your colleague: Some teams share the certificate to manage efficiently. You may get a certificate from your colleague, which should be a .p12 file so that you have a private key to sign the code.

From Xcode: As the beginning of the post says, Xcode provides many convenient ways to manage automatically. In Accounts > Manage Certificates…, you can create a certificate through GUI (Graphical User Interface). Click the plus button and choose the certificate type you want to create. If you don’t see a distribution option, you don’t have permission. If you want to export it, right-click the certificate, enter a strong password, and save it with a .p12 file. Remember that save your .p12 file carefully.

Figure 11. Create a certificate using Xcode

Create provisioning profile

Step 5.
Create a provisioning profile and enable the development certificate you created earlier. Also, make sure to enable your test devices. If you don’t see them, add them to the device list first.

Step 6.
Download the certificate and provisioning profile you created and double-click them to save them on your Mac.

Sign the app code

Step 7.
Xcode uses the codesign tool to sign the app code when you archive your app and package the signature, app code, and provisioning profile into an .ipa file.

Codesign will request permission that use your private key to sign the code for the first time. When you renew the certificate on your CI/CD server, you should notice that.

Step 8.
Install the app on the test device. You can install the app by dragging the .ipa file to the Device and Simulator window in Xcode or by using Apple Configurator or third-party service such as AppCenter or to install it.

Step 9.
When launching the app, the iPhone device performs a set of checks:

  • It checks if the bundle ID and entitlements match.
  • It checks if the UDID of the device is in the device list.
  • It uses the CA’s public key, which is installed by default, to verify if the certificate is trusted. If the certificate is trusted, it uses the user’s public key in the certificate to verify if the code is intact.

Distribution scenario

There are some different in distribution:

  1. Step 2 change to the distribution certificate.
  2. Step 5 change to the distribution provisioning profile. Depending on your requirement, you can choose in-house, ad-hoc, or App Store.
  3. Step 8 change to “submit to App Store” if Step 5 chooses App Store provisioning profile. Once App Store verifies that all check are correct, it will re-sign the app so that each device doesn’t need to check again when installed. As a result, the final app distributed through the App Store doesn’t include the provisioning profile.

That’s it. Thanks for reading. If this article helps you, remember to give me claps. 👏👏👏

Related reads

  1. Apple developer document — TN3125: Inside Code Signing: Provisioning Profiles
  2. Apple developer document — Cryptography Concepts In Depth
  3. Apple developer document — About Code Signing
  4. Apple developer document — Code Signing Tasks
  5. Apple support — App code signing process in iOS and iPadOS
  6. Apple support — What’s a certificate?
  7. Apple support — Intro to certificate management for Apple devices
  8. Apple Xcode help — What is app signing?
  9. objc.io — Inside Code Signing

--

--