Signing and notarizing the wrapper package

Signing the wrapper package with a Developer ID Installer certificate is mandatory. Notarization is a separate Apple verification step and is strongly recommended alongside signing.

Before you begin

Obtain both of the following before you begin.

  • A Developer ID Installer certificate. If the certificate is not already installed, generate a certificate signing request (CSR) in Keychain Access, using Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority. Then, in the Apple Developer portal under Certificates, Identifiers & Profiles > Certificates, create a Developer ID Installer certificate from that CSR, download the resulting .cer file, and double-click it to install it into the Keychain of the build macOS endpoint.
    Note: On an organization account, only the Account Holder, or an Admin with the Certificates, Identifiers & Profiles - Developer ID access role, can create Developer ID certificates.
  • An app-specific password for notarization. This is not your ordinary Apple ID password, which the notarization service rejects, particularly when two-factor authentication is enabled. Generate an app-specific password at appleid.apple.com, under Sign-In and Security > App-Specific Passwords. Apple displays the generated password only once, so copy it before you close the page.
  • Your Apple Developer Team ID. Sign in at developer.apple.com/account and open Membership Details. The Team ID is the 10-character alphanumeric string shown there, and replaces TEAMID everywhere it appears below.

Important: Microsoft's official documentation requires that any .pkg uploaded as a macOS LOB app be signed with a Developer ID Installer certificate obtained from an Apple Developer account. If the package is not signed, Intune accepts the upload, but the install silently fails on every target macOS endpoint when pushed through the MDM channel, even though the same package may install successfully when run manually in Terminal. Notarization ensures that Gatekeeper does not flag the installer as being from an unidentified developer.

Signing embeds a cryptographic signature into the package, generated using a certificate tied to a specific Developer ID or Team, and to a bundle identifier registered under that developer account. This proves to macOS that the package genuinely comes from your organization and has not been tampered with since it was built. Notarization goes a step further: Apple's servers scan the signed package for known malicious content and issue a ticket confirming that it passed the check. Gatekeeper checks for both of these before allowing many types of installers to run without warnings.

The bundle identifier used for signing must be the same reverse-DNS identifier assigned to the wrapper package when it was built (for example, com.forcepoint.websenseendpoint.wrapper). The identifier is recorded in the package receipt on the target macOS endpoint, where it can be confirmed with pkgutil --pkgs. Apple's notarization service tracks each submission by the submission UUID that notarytool returns, not by the bundle identifier.

Steps

  1. On the build macOS endpoint, confirm that the Developer ID Installer certificate is present in Keychain:
    security find-identity -v -p basic
    Look for an entry that reads Developer ID Installer: <Your Organization Name> (<TEAMID>) in the output. This exact name string is used in the signing command. If no such entry is listed, the certificate is not installed on this endpoint; obtain it as described in the prerequisites before continuing.
  2. Sign the package:
    productsign --sign "Developer ID Installer: Your Organization (TEAMID)" \
      WebsenseEndpointInstaller.pkg \
      WebsenseEndpointInstaller-signed.pkg
  3. Verify that the signature was applied correctly:
    pkgutil --check-signature WebsenseEndpointInstaller-signed.pkg
    The output confirms that the package is signed by a developer certificate issued by Apple for distribution, and lists Developer ID Installer in the certificate chain. If it reports Status: no signature, the signing step did not apply correctly and must be repeated before continuing.
  4. Store your notarization credentials in the Keychain, so that the app-specific password is not passed on the command line:
    xcrun notarytool store-credentials "Notarytool" \
      --apple-id "your-apple-id@example.com" \
      --team-id "TEAMID"
    The command prompts for the app-specific password and saves it to the Keychain under the profile name. The notarization commands that follow reference the profile rather than the password itself. You only need to do this once per build endpoint.
    Note: Use the profile name Notarytool exactly as shown. The signing and notarization helper scripts reference the profile by that name.
    Important: Do not pass --password on the command line. The value is written to your shell history and is briefly visible to other local processes through ps. Where a stored profile cannot be used, such as in an automated build, supply the password through an environment variable instead, using --password "@env:NOTARY_PASSWORD".
  5. Confirm that the stored profile authenticates:
    xcrun notarytool history --keychain-profile "Notarytool"
    An empty list is expected on a new profile. An authentication error means that the Apple ID, Team ID, or app-specific password was not accepted; repeat the previous step before continuing.
  6. Submit the signed package to Apple's notarization service:
    xcrun notarytool submit WebsenseEndpointInstaller-signed.pkg \
      --keychain-profile "Notarytool" \
      --wait
    The --wait flag causes the command to stay running and report back once Apple's automated review completes. This typically takes a few minutes.
    A result of status: Accepted confirms that notarization succeeded. If the result is Invalid, request the detailed log for troubleshooting:
    xcrun notarytool log <submission-id> --keychain-profile "Notarytool"
  7. Staple the notarization ticket to the package so that it can be verified offline, without contacting Apple's servers, at install time:
    xcrun stapler staple WebsenseEndpointInstaller-signed.pkg
    Important: Do not alter the package in any way after stapling. Re-compressing it, unpacking and repacking it, or re-signing it strips or invalidates the stapled ticket, and the package must then be signed, notarized, and stapled again. Upload the stapled file to Intune exactly as this step produces it.
  8. Confirm that both signing and notarization are correctly in place:
    spctl -a -vvv -t install WebsenseEndpointInstaller-signed.pkg
    A successful result reads accepted and lists the source as Notarized Developer ID. A result of rejected with source=Unnotarized Developer ID means the package is signed but was never successfully notarized.
  9. Confirm that the notarization ticket is stapled to the package:
    xcrun stapler validate WebsenseEndpointInstaller-signed.pkg
    A successful result reads The validate action worked!

    A stapled ticket travels inside the file, so Gatekeeper can approve the package without contacting Apple. Without it, the target endpoint must reach Apple's notarization servers at the moment of install, which an MDM-triggered install cannot be relied upon to do. This check therefore matters more for Intune deployments than for an interactive install.

Next steps

Note: WebsenseEndpointInstaller-signed.pkg, after stapling, is the file to upload to Intune. Do not upload the original unsigned WebsenseEndpointInstaller.pkg.
Important: Intune neither signs nor notarizes the packages that it receives. It distributes the package exactly as uploaded. A package that was never signed, or whose ticket was stripped after stapling, is still accepted at upload and then fails silently on every target macOS endpoint. The local spctl check above is the last point at which this is detectable.
Note: Repeat this entire procedure for every new build. Signatures and stapled notarization tickets do not carry over from a previous build, even one that is otherwise identical.