With the release of macOS Catalina (10.15) there have been some changes to the way software needs to be signed. In particular, Apple has introduced a new ‘Notarization’ service which allows developers to pre-register the digital signature of an application so that it can be later checked by the operating system when a user downloads software outside the App Store.
Earlier versions of macOS still required software to be signed by the developer to allow it to run on a user’s system, however notarization takes this one step further, and presumably allows Apple to disable malicious applications when macOS phones home to check if the app has been notarized.
If you’re using Xcode for development and packaging of your app, this is all handled for you in the latest versions. Unfortunately, CSView is written in Java, requiring a custom build and packaging process, and this process has not survived the update to Catalina. It appears that some of the existing configuration was not compatible with the new sandboxing requirements in Catalina, so simply notarizing the old versions of the app was not sufficient, even for versions distributed through the App Store.
I took this as a good opportunity to update to Java 11, and the rest of this article covers the changes I’ve needed to make to the packaging process.
Packaging a Java 11 macOS App
Java 8 included a tool called javapackager which allowed an application written in Java to be bundled into an installable package on several different operating systems, including macOS. This was the approach I originally took with CSView and it worked well, creating both a .dmg file containing the bundled application and a signed package ready for submission to the App Store. Unfortunately this tool was removed in version 11 of the JDK, with the replacement tool jpackage still in development and not provided as part of the standard distributions of the JDK (it may be included with Java 14).
Following the guide Using the Java Packager with JDK 11 by Adam Carroll, I was able to get the packaging working again with Java 11. This has the nice benefit that with the post Java 9 support for modules, the app bundle is now about 30% smaller.
Signing and Notarizing for macOS Catalina
Unfortunately the available versions of the tool have not yet been updated to deal with the new more stringent signing requirements for Catalina. This means that I have needed to replace the signing step supported by
jpackage, and to then generate the app installation packages manually from that signed app.
Several hours of experimentation later, and I have arrived at the following changes needed for Catalina:
- Enable the hardened runtime when signing. This is done by adding the
—options runtimeflag to
- Enable the secure timestamp when signing, using the
- Add some exceptions to the sandbox using the entitlements file. The ones which seem to be needed are:
<key>com.apple.security.cs.allow-jit</key> <true/> <key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/> <key>com.apple.security.cs.disable-executable-page-protection</key> <true/> <key>com.apple.security.cs.allow-dyld-environment-variables</key> <true/> <key>com.apple.security.cs.disable-library-validation</key> <true/>
The signing command now looks like this:
codesign --force --deep \ --options runtime \ --timestamp \ --prefix [SIGNING PREFIX] \ --entitlements [APP ENTITLEMENTS FILE] \ --sign "[DEVELOPER SIGNING KEY]" \ "[FILE TO SIGN]"
You’ll need sign all dylib files and executables in your app.
Once your app is signed, you can then use the
productbuild command to build a package for the app store. The command should look something like this:
productbuild --component [APP BUNDLE] /Applications \ --sign "[INSTALLER SIGNING KEY]" \ --product [APP BUNDLE]/Contents/Info.plist \ [OUTPUT PACKAGE]
Which keys should I use?
- If you’re building a .dmg or .pkg file for distribution outside the app store, you’ll need to use the
Developer ID Applicationand
Developer ID Installerkeys. These will be accepted by the Notarization service.
- If you’re building a .pkg file for upload to the app store, you should use the
3rd Party Mac Developer Applicationand
Installerkeys. These will be accepted by App Store Connect when uploading your app.
Note that you don’t need to notarize your app if you’re distributing it via the App Store, it’s just for apps being distributed directly to users. In fact, the notarization service will complain that the ‘3rd Party’ keys are not valid for signing the app.
Notarization requires that you upload your signed app package to Apple, who will check the app. You can perform the upload from the command line using the
xcrun altool command:
xcrun altool --notarize-app \ --primary-bundle-id [BUNDLE ID] \ --file [DMG FILE] \ -u [APP STORE CONNECT USERNAME] \ -p [APP STORE CONNECT PASSWORD]
Once notarization is complete you can check the results using the same tool:
xcrun altool --notarization-info \ [NOTARIZATION UUID] \ -u [APP STORE CONNECT USERNAME] \ -p [APP STORE CONNECT PASSWORD]
App Store Connect Upload
There used to be a tool called Application Loader which was used to upload a package to App Store Connect. This has now been removed in Xcode 11, and instead the same
altool command line tool is used as with notarization:
xcrun altool —upload-app -f [APP STORE PACKAGE] \ -u [APP STORE CONNECT USERNAME] \ -p [APP STORE CONNECT PASSWORD]
The password in the above commands will need to be a single-use application password if you’re using MFA on your App Store Connect account, which I think is now compulsory. You can store the password in your keychain to avoid including it in build scripts. For more information, take a look at the Customizing the Notarization Workflow article on the Apple developer website.
That’s it! Hopefully these notes will help someone else avoid searching around the details scattered across several sites and articles. Let me know if this does/doesn’t work for you, or if you have any improvements to the process.