You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

10 KiB

Signing and notarizing distributions for macOS

Apple requires all 3rd party apps to be signed and notarized (checked by Apple) for running on recent versions of macOS.

What is covered

In this tutorial, we'll show you how to sign and notarize native distributions of Compose apps (in dmg or pkg formats) for distribution on macOS.

Prerequisites

  • Xcode. The tutorial was checked with Xcode 12.3.
  • JDK 15+. The tutorial was checked with OpenJDK 15.0.1.

Preparing a Developer ID certificate

You will need a Developer ID certificate for signing your app.

Checking existing Developer ID certificates

Open https://developer.apple.com/account/resources/certificates

Creating a new Developer ID certificate

  1. Create a certificate signing request:
    • Open Keychain Access.
    • Open the menu dialog
      Keychain Access > Certificate Assistant > Request a Certificate from a Certificate Authority
      
    • Enter your Developer ID email and common name.
    • Check Save to disk option.
  2. Create and install a new certificate using your Apple Developer account:
    • Open https://developer.apple.com/account/resources/certificates/add
    • For publishing outside the App Store, choose the Developer ID Application certificate type. For publishing on the App Store, you need two certificates. First select the Mac App Distribution certificate type, and once you have completed the steps in this section, repeat them again for the Mac Installer Distribution certificate type.
    • Upload your Certificate Signing Request from the previous step.
    • Download and install the certificate (drag & drop the certificate into the Keychain Access application).

Viewing installed certificates

You can find all installed certificates and their keychains by running the following command:

/usr/bin/security find-certificate -c "Developer ID Application"

or the following commands when publishing on the App Store:

/usr/bin/security find-certificate -c "3rd Party Mac Developer Application"
/usr/bin/security find-certificate -c "3rd Party Mac Developer Installer"

If you have multiple developer certificates of the same type installed, you will need to specify the path to the keychain, containing the certificate intended for signing.

Preparing an App ID

An App ID represents one or more applications in Apple's ecosystem.

Viewing existing App IDs

Open the page on Apple's developer portal.

Creating a new App ID

  1. Open the page on Apple's developer portal.
  2. Choose App ID option.
  3. Choose App type.
  4. Fill the Bundle ID field.
    • A bundle ID uniquely identifies an application in Apple's ecosystem.
    • You can use an explicit bundle ID a wildcard, matching multiple bundle IDs.
    • It is recommended to use the reverse DNS notation (e.g.com.yoursitename.yourappname).

Preparing a Provisioning Profile

For testing on TestFlight (when publishing to the App Store), you need to add a provisioning profile. You can skip this step otherwise.

Checking existing provisioning profiles

Open https://developer.apple.com/account/resources/profiles/list

Creating a new provisioning profile

  1. Open the page on Apple's developer portal.
  2. Choose Mac App Store option under Distribution.
  3. Select Profile Type Mac.
  4. Select the App ID which you created earlier.
  5. Select the Mac App Distribution certificate you created earlier.
  6. Enter a name.
  7. Click generate and download the provisioning profile.

Creating an app-specific password

To be able to upload an app for notarization, you will need an app-specific password associated with your Apple ID.

Follow these steps to generate a new password:

  1. Sign in to your Apple ID account page.
  2. In the Security section, click Generate Password below App-Specific Passwords.

See this Apple support page for more information on the app-specific passwords.

Adding an app-specific password to a keychain

To avoid remembering your one-time password, or writing it in scripts, you can add it to the keychain by running:

# Any name can be used instead of NOTARIZATION_PASSWORD

xcrun altool --store-password-in-keychain-item "NOTARIZATION_PASSWORD"
             --username <apple_id>
             --password <password>

Then you'll be able to refer to the password like @keychain:NOTARIZATION_PASSWORD without the need to write the password itself.

Configuring Gradle

Gradle DSL

DSL properties should be specified in macOS DSL block of Compose Desktop DSL:

import org.jetbrains.compose.compose
import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
    kotlin("jvm")
    id("org.jetbrains.compose")
}

dependencies {
    implementation(compose.desktop.currentOS)
}

compose.desktop {
    application {
        mainClass = "example.MainKt"

        nativeDistributions {
            targetFormats(TargetFormat.Dmg)

            macOS {
                // macOS DSL settings
            }
        }
    }
}

Gradle properties

Some properties can also be specified using Gradle properties.

  • Default Gradle properties (compose.desktop.mac.*) have lower priority, than DSL properties.
  • Gradle properties can be specified (the items are listed in order of ascending priority):
    • In gradle.properties file in Gradle home;
    • In gradle.properties file in project's root;
    • In command-line
     ./gradlew packageDmg -Pcompose.desktop.mac.sign=true
    
  • Note, that local.properties is not a standard Gradle file, so it is not supported by default. You can load custom properties from it manually in a script, if you want.

Configuring bundle ID

macOS {
    bundleID = "com.example-company.example-app"
}

A bundle ID uniquely identifies an application in Apple's ecosystem.

  • A bundle ID must be specified using the bundleID DSL property.
  • Use only alphanumeric characters (A-Z, a-z, and 0-9), hyphen (-) and period (.) characters.
  • Use the reverse DNS notation of your domain (e.g. com.yoursitename.yourappname).
  • The specified bundle ID must match one of your App IDs.

Configuring signing settings

macOS {
    signing { 
         sign.set(true)
         identity.set("John Doe")
         // keychain.set("/path/to/keychain") 
    }
}
  • Set the sign DSL property or to true.
    • Alternatively, the compose.desktop.mac.sign Gradle property can be used.
  • Set the identity DSL property to the certificate's name, e.g. "John Doe".
    • Alternatively, the compose.desktop.mac.signing.identity Gradle property can be used.
  • Optionally, set the keychain DSL property to the path to the specific keychain, containing your certificate.
    • Alternatively, the compose.desktop.mac.signing.keychain Gradle property can be used.
    • This step is only necessary, if multiple developer certificates of the same type are installed.

The following Gradle properties can be used instead of DSL properties:

  • compose.desktop.mac.sign enables or disables signing. Possible values: true or false.
  • compose.desktop.mac.signing.identity overrides the identity DSL property.
  • compose.desktop.mac.signing.keychain overrides the keychain DSL property.

Those properties could be stored in $HOME/.gradle/gradle.properties to use across multiple applications.

Configuring notarization settings

macOS {
    notarization { 
         appleID.set("john.doe@example.com")
         password.set("@keychain:NOTARIZATION_PASSWORD")
         
         // optional
         ascProvider.set("<TEAM_ID>")
    }
}
  • Set appleID to your Apple ID.
    • Alternatively, the compose.desktop.mac.notarization.appleID Gradle property can be used.
  • Set password to the app-specific password created previously.
    • Alternatively, the compose.desktop.mac.notarization.password Gradle property can be used.
    • Don't write raw password directly into a build script.
    • If the password was added to the keychain, as described previously, it can be referenced as
    @keychain:NOTARIZATION_PASSWORD
    
  • Set ascProvider to your Team ID, if your account is associated with multiple teams.
    • Alternatively, the compose.desktop.mac.notarization.ascProvider Gradle property can be used.
    • To get a table of team IDs associated with a given username and password, run:
xcrun altool --list-providers -u <Apple ID> -p <Notarization password>"

Configuring provisioning profile

For testing on TestFlight (when publishing to the App Store), you need to add a provisioning profile. You can skip this step otherwise.

Note that this option requires JDK 18 due to this issue.

macOS {
    provisioningProfile.set(project.file("embedded.provisionprofile"))
}

Make sure to rename your provisioning profile you created earlier to embedded.provisionprofile.

Using Gradle

The following tasks are available:

  • Use createDistributable or packageDmg to get a signed application (no separate step is required).
  • Use notarize<PACKAGING_FORMAT> (e.g. notarizeDmg) to upload an application for notarization. Once the upload finishes, a RequestUUID will be printed. The notarization process takes some time. Once the notarization process finishes, an email will be sent to you. Uploaded file is saved to <BUILD_DIR>/compose/notarization/main/<UPLOAD_DATE>-<PACKAGING_FORMAT>
  • Use checkNotarizationStatus to check a status of last notarization requests. You can also use a command-line command to check any notarization request:
xcrun altool --notarization-info <RequestUUID> 
             --username <Apple_ID>
             --password "@keychain:NOTARIZATION_PASSWORD"