Seeing Through the Apple Signing Jungle

Signing apps can be an overwhelming topic when you don’t let Xcode handle it automatically. This is an attempt to summarize it in my own words like I would do to others in a casual setting who are not familiar with the topic yet.

I do not remember every hearing about code signing for the first five years which I was into programming since 2005. Nowadays the idea of running unsigned code from some online source sounds scary to me. Code signing serves the purpose to verify its origin and publisher. Also it is used to detect malicious tampering.

Things are simple when you have one app you directly publish yourself and let Xcode automatically manage the signing. Things are complicated when you have an app with different builds for different customers who have different development team IDs and different app identities.

When I started to dig into this topic I often was left confused. The ambiguous user interface of the Apple developer account was part of the problem. After some time I finally saw through it. Now it is difficult for me to get back into the past mindset and remember what actually was difficult to grasp. So this is an attempt to condense the information into a quick summary. Like many abstractions and generalizations this will sacrifice detail for simplicity.

Assets

There some “things” which need to be organized for signing. You can find them in your developer account in the section “Certificates, Identifiers & Profiles”.

Development Teams

If you are a registered developer you likely have your own membership or are part of a team or enterprise program. Your personal developer account can also be part of multiple teams. Everything that follows belongs into such accounts and is related to those.

Certificates

Whatever kind your membership is of, there may be certificates. They actually consist of two parts: the certificate and related private key. You definitely need the private key to sign things. Having only the certificate allows only the verification of a signature. Important: Keep the private key a secret key to your self because otherwise people can sign code in your name and pretend they are you. Certificates should be personal and not shared.

There are multiple types and their differences are not always clear. The currently most interesting ones are Apple Development and Apple Distribution certificates. As far as I know they supersede the platform specific versions like the Mac Development certificates. Of course there are a lot more but those details are not relevant for now.

You need to have such certificate and the related private key installed in the keychain of the Mac you are building or signing the app with.

Identifiers

Identifiers are like license plates for cars. They unambiguously identify an app. A practical example of their use: the prevent others from publishing an app with the identity of yours and take over (in example) user data. Features like sandboxing build on top of those identifiers. A potential downside: You can build your codebase with different IDs. This technically is completely fine but can be very confusing even for developers and testers inside a company. On macOS your built products look completely identical (unless you inspect the bundle or container in the Terminal) but technically they are completely different and unrelated apps.

A subtopic are “capabilities” or also called “entitlements” of apps. They are tied to identifiers. For example push notifications. If you want to enable those for your app, they must also be enabled for the app ID in use on the developer portal. I would consider this out of scope for this summary.

Devices

You can register devices with your developer account. Owners of those devices need to actively participate in that process. This is necessary, if you want to deploy development builds on those. The number of devices you can register in your account is limited and can vary. This also is a security measure to prevent malicious publishers to distribute code with only development signing.

Provisioning Profiles

Provisioning profiles are like glue between certificates, identifiers and devices. Or in different words: a document which brings the aforementioned things together in a practical constellation.

A provisioning profile basically describes who (certificate) can distribute what (identifiers) on what (devices).

An annoying aspect of provisioning profiles in combination with manual signing: every time the certificates, included identifiers or list of devices you want to deploy to changes you need to regenerate the provisioning profiles. But that is what the automatic management by Xcode is there for.

Setting up Manual Signing

So how does this work out in practice? I will assume the following situation from real experience:

  • I have a developer account in a team which is part of the enterprise program.
  • There is a macOS app with a Finder Sync extension I am working on.
  • Bespoke app is brandable which means: I can build customized and specific variations for each of our customers.
  • The project is configured for manual signing.
  • I want to distribute development build in the company for testing by the QA team.
  • Those builds should be signed in a way that does not require registration of devices and does not cause any malware warnings.

First I need the appropriate certificate. Under these circumstances a certificate of the “Developer ID Application” type is appropriate. It is the type one usually uses to sign and distribute macOS apps outside the Mac App Store. With this it is not required to register any devices with your account, you can distribute to all devices out there. Also it causes macOS' Gatekeeper not to warn or even hinder you when trying to launch the app. When you are part of a development team only an account holder can issue certificates of this type.

Following up I create two app IDs. One is for the app I am working on and the other for the Finder Sync extension. Extensions are packaged in their own bundle which is embedded in the host app. For the purpose of this article I will call them org.example.app and org.example.app.extension.

Next I have to create provisioning profiles. One for each app ID. Similar to the different types of certificates the first question is about the type of the provisioning profiles. The provisioning profile types should correspond to the certificate type. This means: “Developer ID” under “Distribution”. Then it needs to be associated with a single identifier. In this case I chose the previously created identifier for the app, org.example.app. Finally the certificate to refer to needs to be selected. This needs to be repeated for the Finder Sync extension. The result are two provisioning profiles which state that the person in control of the named certificate is enabled to sign an app with the named identifier.

The certificate, its private key and the provisioning profiles must be deployed on the build machine. In detail this means: the certificate and the private key need to be added to the keychain and the provisioning profiles available in Xcode. The latter usually happens automatically when the developer account is signed in in Xcode. Otherwise they can manually be placed in:

~/Library/MobileDevice/Provisioning Profiles

Closing Words

I hope this helped at least a bit. For sure this is not a complete and definitive explanation. But hopefully it is another brush stroke added to the picture of understanding.

About The Author

Peter Thomas Horn is a professional software developer at Open-Xchange specialized on the Apple platform. He previously worked a decade across the full stack of various web technologies. Originally started with Java on Windows at the age of 12 years. While staying humble in throwing around buzzwords like "VR" and "machine learning" he occasionally experiences problems and the fitting solutions considered worth sharing.