Building Trusted Software for macOS: A how-to guide for digital signing

The App Store is a powerful force in the digital world, serving as a portal to a massive user base. Today, there are over 2.2 billion active Apple devices worldwide. Apple’s App Store alone attracts more than 650 million weekly active users worldwide. Often, however, it makes sense to bypass the App Store and deliver your applications directly to your users, for example, via a download from your web site.
The downside of direct distribution are the annoying pop-ups and alerts that can plague users trying install and execute apps. To prevent that experience, you must digitally sign your app to make it trusted by macOS.
In this post, we’ll show you how to deliver trusted macOS apps, without the added overhead of distributing via the App Store.
Gatekeeper
To protect Apple users against untrustworthy apps, macOS relies on technology called ‘Gatekeeper’. Gatekeeper scans software from any source, from an app store, an arbitrary website, an email attachment, wherever. The application that downloads the file (that browser or email client) adds the file attribute com.apple.quarantine
. The quarantine attribute alerts Gatekeeper to both verify the digital signature, and to check whether it’s notarized, before executing it. We’ll discuss notarization later. But the critical point is that your software must be digitally signed by a private key associated with a trusted X.509 certificate.
Formats
Applications on macOS are saved in the .app
format, a file system folder with a specific layout. If you want to distribute software, you package the .app
folder using one of two container formats:
Disk Images (
.dmg
): Easiest when distributing a single file/app.Installer packages (
.pkg
): Allows for specialized scenarios, such as running code during the installation.
(Refer to Apple’s official guide to packaging applications for more details.)
Prereqs for Code Signing
First, make sure you have:
An Apple developer account. It costs $99 a year.
Xcode
Xcode developer tools.
Run
xcode-select --install
on the command line
Code Signing Certificates
Apple software is signed using x.509 code signing certificates.
Certificate Signing Requests
Code Signing Certificates link metadata, such as the name and place of registration of your company, to an asymmetric, public/private key pair. You can request a certificate from a Certificate Authority (CA), providing the metadata and the public key. This request is called a Certificate Signing Request (CSR); the CA verifies that the metadata is correct before issuing and signing the certificate.
The private key remains under your control. It must be securely stored. Anybody in possession of the private key can sign software in the name of your organization.
Storage of the Private Key
Apple’s documentation refers to creating the keypair on a Mac and storing it in the local keychain, protected by a password. To make the keys available on other build/developer machines, you have to share the key pair. Popular tools, such as fastlane, recommend storing certificates and encrypted private keys in a central location where they can be accessed by any developer. Of course, this introduces risk. It makes it easier for an attacker to steal the key file and encryption password to sign any software in your company’s name! Since no further authentication is required, this attack vector is especially dangerous when publishing software outside the confines and relative security of the App Store.
In the Windows world, the CA/Browser forum mandates that private keys for code signing certificates are generated on Hardware Security Modules (HSM) where they are protected from theft. It’s a good idea to follow these guidelines for Apple software as well.
Cloud-based code signing solutions (SignPath, for one) make it easy to use an HSM for code signing. SignPath provides an option, via the web-based console, to generate a certificate signing request (CSR) for a private key generated on hardware. It’s a simple point-and-click operation, with absolutely zero configuration or provisioning hassle.
Creating a Test Certificate
These days, everyone builds software on continuous integration systems. No matter which system you use, it’s a good idea to bake code signing into the build process itself. This will minimize the hassle of doing manual signing or writing scripts at build and release time.
But you probably don’t want to sign all your nightly builds with an official Apple code signing certificate. It’s easier to just use a test certificate. One hitch is that you need to configure your system to trust that test certificate.
To make it trusted, you can install the test certificate on a keychain in your macOS system and enable the “Code Signing” trust setting. Gatekeeper will still identify and complain about the missing notarization though. If you’re doing manual testing, you can right-click the application and ignore the warning. If you’re testing programmatically, you can remove the quarantine flag. Use the following command to avoid Gatekeeper checks altogether:
Important: Apple’s codesign
tool, which is used for signing software, does not require certificates to be issued by Apple. Package Installers (.pkg
) are a notable exception. The .pkg
format only supports official certificates by Apple. Test certificates cannot be used.
Creating a Release Certificate
If you want all the Apple devices in the world to trust your application, you need an official certificate from Apple. Those certificates are trusted by default on all Apple devices. You can get an official Apple certificate from the developer portal. Apple supports a variety of certificates. Depending on the chosen distribution format, you need to create either a Developer ID Installer (for .pkg
) or a Developer ID Application (for .dmg
and .app
) certificate.