Skip to content

Codesigning On macOS

Patrick Heyer edited this page Jan 7, 2025 · 4 revisions

Contents

  1. Code Signing On macOS
  2. Types Of Code Signatures
  3. How To Apply Code Signatures
  4. Notarization
  5. Repercussions for OBS Studio
  6. Change Code Signing Settings
  7. Setting Up Code Signing For GitHub Actions
  8. Generating Codesigning Certificates Without An Apple Device

Code Signing On macOS

Codesigning was introduced by Apple first on iOS and later macOS to associate distributed code via its own AppStore with a registered developer but also detect any possible tampering to an application (as it would make the signature invalid).

By default macOS will present a warning if the user attempts to either run an unsigned application or an application itself attempts to load code at runtime (as OBS Studio does with plugins) that are not signed themselves. In addition to this, starting with macOS 11 all binaries are required to be code signed to run on Apple Silicon Macs.

Types Of Code Signatures

To distribute macOS binaries with a valid code signature, an Apple Developer membership is required which carries a yearly subscription fee of $99 (local prices can differ).

With a valid Apple Developer membership, binaries as well as installer packages can be codesigned, distributed, and will meet the requirement for running on Apple Silicon Macs.

Without a valid membership, the only other option is to apply an "ad-hoc" signature to a binary. This will require each user to sign the binary themselves after downloading them to their local machine.

Warning

Each ad-hoc signature is only valid for the machine on which it was applied. It will also only meet the minimum requirement to run the binary, it will not disable any warnings presented by macOS and will also require the user to explicitly allow the code to run.

How To Apply Code Signatures

To apply a code signature to a binary (application or library) use the codesign command-line tool. To codesign an installer package use productsign instead.

Example: Codesign With Developer ID Certificate

The "Developer ID Application" certificate needs to be downloaded from the Apple Developer portal and installed on the local system. The certificate will carry a name along the lines of Developer ID Application: <FIRSTNAME> <LASTNAME> (<LETTERS_AND_NUMBERS>).

Once the certificate is installed, a binary can be signed using the following command:

codesign --sign "<YOUR CERTIFICATE NAME>" <Path-To-Your-Plugin-Bundle>

macOS will recognize a valid plugin bundle and will know where to find the actual binary that needs to be codesigned.

Note

If there are other binaries inside the bundle (e.g. other dynamic libraries), those need to be codesigned first following the "inside-out" principle: First all binaries inside the bundle need to be signed before the outer bundle can be signed.

Example: Codesign Package Installer With Developer ID Certificate

Just like before a valid installer certificate needs to be installed on the local system. The "Developer ID Installer" certificate available in the Apple Developer portal is required for this. Once installed on the local system, the package installer can be signed using the following command:

productsign --sign "<YOUR INSTALLER CERTIFICATE NAME>" <Path-To-Your-Plugin-Package>

Example: Codesign With An Ad-Hoc Signature

Applying an ad-hoc signature just requires using a dash (-) as the name of the code signature like so:

codesign --sign - --force <Path-To-Your-Plugin-Bundle>

The additional --force argument ensures that whichever signature might be present (potentially the ad-hoc signature generated and used by the developer) is replaced with one valid for the local machine.

Notarization

Beginning with macOS 10.15, Apple also began to require all applications not distributed via the AppStore to be "notarized". This will upload the binary to Apple's servers for static code analysis and checks for proper code signatures (and whether all executable elements of an application are properly signed). Once this succeeds, the binary will be "stamped" with the notarization and thus pass additional checks done by macOS. Any tampering with the binary will invalidate this stamp.

Notably only binaries signed with a valid Apple Developer ID can be notarized, thus any binary signed with an ad-hoc signature will never pass the notarization checks.

When working with Xcode, the notarization process can be done from within the IDE by following Apple's documentation. Alternatively the process can be executed entirely from the command-line, once again following Apple's documentation for manual notarization.

Repercussions for OBS Studio

For the time being official OBS Studio releases will be code signed and notarized with a specific exception to allow it to load modules that are not signed with the same certificate (which is the default requirement for applications that opt in to the "hardened runtime"). This allows OBS Studio to load plugins signed by different developers.

This will not allow OBS Studio to load unsigned plugins on Apple Silicon-based Macs. Users thus need to be instructed to apply an ad-hoc signature themselves to the plugin to enable it to load.

Change Code Signing Settings

Usually the code signing configuration can be done from within Xcode easily, which can be either set up to only apply an ad-hoc signature (identified by the choice to "Sign to Run Locally"), use a Apple Developer ID certificate that was download and installed on the local machine, or let Xcode handle all code signing automatically (which is by far the easiest choice).

Xcode_Code_Signing_Settings

Warning

If CMake is used as the build system generator for the plugin, codesigning information should be provided to CMake directly, as it will otherwise overwrite any changes made in Xcode, thus resetting the codesigning configuration.

To provide CMake with the necessary information for automatic codesigning, simply provide your Apple Developer Team ID to it by setting the CODESIGN_TEAM CMake variable. Providing a CODESIGN_IDENTITY instead will disable automatic signing and use manual signing with the provided identity, but will require a matching certificate to be installed on the local machine.

Tip

A valid developer certificate will usually carry a name in the form of Developer ID Application: <FIRSTNAME> <LASTNAME> (<LETTERS_AND_NUMBERS>). Either provide this entire name as CODESIGN_IDENTITY or just the LETTERS_AND_NUMBERS part as the CODESIGN_TEAM.

Setting Up Code Signing For GitHub Actions

The plugin template supports code signing on GitHub Actions out of the box. The following steps set up all necessary information to enable this behavior:

  • First, the locally stored developer certificate needs to be exported from the macOS key chain:
    • Using the Keychain app on macOS, two certificates (the Application and Installer certificate) with their public and private keys need to be exported to a single .p12 file protected with a strong password
    • The the .p12 file then needs to be encoded into a base64 representation by running base64 <NAME_OF_P12_FILE>
  • Next, the certificate data and the password used to export it need to be set up as repository secrets:
    • MACOS_SIGNING_APPLICATION_IDENTITY: Name of the "Developer ID Application" signing certificate
    • MACOS_SIGNING_INSTALLER_IDENTITY: Name of "Developer ID Installer" signing certificate
    • MACOS_SIGNING_CERT: The base64-encoded .p12 file
    • MACOS_SIGNING_CERT_PASSWORD: Password used to generate the .p12 certificate
  • To also enable notarization on GitHub Action runners, the following repository secrets are required as well:
    • MACOS_NOTARIZATION_USERNAME: Your Apple Developer account's Apple ID
    • MACOS_NOTARIZATION_PASSWORD: Your Apple Developer account's generated app password

Beware Of Newlines In Your Base64 String

By default the base64 command-line tool might use formatting to generate its output and also add a final newline character. To avoid this, the openssl command-line tool can be used as an alternative:

cat <PKCS12 archive> | openssl base64 -A

The generated base64-encoded string (either printed to the terminal or optionally redirected to a file) will be zero-terminated and not introduce an unwanted newline character.

Caution

DO NOT USE YOUR APPLE ID PASSWORD FOR NOTARIZATION!

Create a new temporary "app password" for use on CI instead.

Generating Codesigning Certificates Without An Apple Device

While it is necessary to have an Apple Developer account to create and manage codesigning certificates as well as provisioning profiles, it is not strictly necessary to own or use an Apple device to do so.

For the purposes of a plugin, a developer needs to create and maintain at the very least:

  • A certificate to sign the plugin itself (called "Developer ID Application")
  • A certificate to sign the package installer (called "Developer ID Installer")
  • A PKCS #12 certificate archive that will contain the private keys and associated application and installer certificates

Creating Certificates

To create certificates, follow the official Apple instructions on the topic.

Tip

Apple's instructions refer to another set of "instructions to create a certificate signing request" as step number 4, which do require a macOS device. As part of this guide, step number 4 is to be replaced with the instructions below.

An appropriate certificate signing request (CSR) can be created using openssl on the command line:

# First generate a 2048-bit RSA private key
openssl genrsa -out Apple_Signing_Certificate_Private_Key.key 2048

# Second, generate the actual CSR
openssl req -out Apple_Signing_Certificate_CSR.csr -key Apple_Signing_Certificate_Private_Key.key -new -sha256

Caution

This private key file has to be retained, as it's the key associated with the created developer certificates and is also necessary to generate the PKCS #12 archive mentioned above. When using macOS, the CSR and key are automatically stored in the keychain.

With the CSR and private key created locally, the remaining steps in Apple's instructions can now be followed as-is.

Important

Without access to a Mac, the same private key needs to be used to create the CSR for the "Developer ID Installer" certificate (due to a limitation of openssl).

Generate The PKCS12 Certificate Archive

The PKCS #12 certificate archive is commonly created using macOS' Keychain application but can also be generated using openssl via the following commands.

# Concatenate the application and installer certificates
cat <Filename of Developer ID Application Certificate> <Filename of Developer ID Installer Certificate> > Github_Actions_Certificates.pem

# Create the PKCS #12 certificate archive
openssl pkcs12 -legacy -export -in Github_Actions_Certificates.pem -inkey Apple_Signing_Certificate_Private_Key.key -out Github_Actions_Certificates.p12

Note

By default separate private keys are used for each certificate when using macOS' built-in tools, but openssl's command line interface is incapable of storing multiple certificate and private key pairs in PKCS #12 archives, thus the same private key has to be used.

The generated file can be used for the steps outlined in Setting Up Code Signing For GitHub Actions.