Signing & notarizing java bundles as macOS apps
Distribute Java Application from B4J for the macOS
Once you have written a Java Application with B4J you want to distribute it to the users computers. When it comes to macOS this can be a tedious process. First of all you don’t want to be dependent on the different java jre installations. A user might or might not have the correct java runtime installed. So you would be happy to distribute your jar in a package, that includes all the necessary java stuff. Here B4J offers the B4JPackager11. With this tool you can build a package that includes everything and the user only needs to doubl-click a batch file to start your B4J java program on the mac. The backside of this solution is that it is not very mac like. The user doesn’t have a real app he/she can open and run. At this point it would be nice to put the package in a real bundle as a macOS app. The easiest way to do this is via the Platypus tool. This is a tool that allows scripts (and java packages) to be bundled as an app. Although it is not a 100% real mac app ( because the program runs in a background shell) it comes very, very close to the real thing. Once you have your macOS App you can distribute it.
As Apple got very severe when it comes to downloading software from the Internet, this process has its own obstacles (e.g. Gatekeeper Path Randomization). Since macOS Catalina apps need to be signed and notarized in order to distribute them smoothly. So you need to pack your App in a .dmg or .pkg file and sign both app and .dmg and lastly notarize the .dmg. For me this was the only way how I could/can distribute my Java-App for the Mac under Catalina.
Here is how I did it:
All following steps are shown with my application „GiroScan“.
1. Create the java package with B4JPackager
Copy the B4JPackager11 to your mac and let it run as described in Erel’s description (I used Java 14):
<java 14>\bin\java -jar B4JPackager11.jar <json file>
My example with GiroScan:
../jdk-14.0.1/Contents/Home/bin/java -jar B4JPackager11.jar giroscan_conf.json
For help the giroscan_conf.json looks like this:
The result looks something like this, run.command the command to start the app:
2. Create an App bundle from the package
As next step we will build an app bundle. This is being accomplished with the tool „Platypus“.
I make it quick here, just follow the entries in the screenshot.
Save your time: One of the more time consuming things is to find a tool that converts your image in an .icns file.
Try this one:
The result from Platypus should look something like this. A program icon that you can double-click in order to start it.
The created program is not a 100% authentic macOS App. When you start it, the App-info states „Java“ and not the app-name. This observation was already mentioned in the B4J forum by user Markos. As long as you do not target Apple’s app store this is only a cosmetic „detail“.
3. Sign the App
By now you should have an App from Platypus, which you need to sign as next step. In order to do this, you need to have an Apple Developer license. Unfortunately this is not for free. I costs 99 USD. Yes, distributing apps has become a business model. It is one of the down sides of the combat against malware. We can’t change it, if we want our apps to run on mac or windows (yes, it’s not much different on Windows, I just spent around 100 bucks for a signing certificate for my app there too!). Next you need to have Xcode, so install it if you do not have it on your Mac.
Include apple specific permissions to your app. You can add these permissions in Xcode directly. I used these:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
Next step sign the bundle:
codesign -s "Developer ID Application: David Meier (6S6G98XRY9)" --timestamp --options runtime -f --entitlements entitlements.plist --deep GiroScan3.app
Then sign single libraries:
sudo codesign -s "Developer ID Application: David Meier (6S6G98XRY9)" --timestamp --options runtime -f --entitlements entitlements.plist --deep /Users/dmeier/Documents/IT/b4j/signing/GiroScan3.app/Contents/Resources/lib/*
It is possible that you will get errors in the notarizing process stating that certain libs or component are not signed. If this happens come back here and sign the missing elements with the above command.
Test whether the bundle is signed:
codesign -vvv --deep --strict GiroScan3.app
4. Create a signed and notarized .dmg file
Once you have signed the app bundle you need to package it for storing and downloading from a web- or ftp-server. I first tried to do this with a .pkg package. This would be the easiest. But I couldn’t get it run correctly. Though signed and notarized the .pkg file failed to be recognized correctly. As I needed an imediate solution I tried with .dmg. And I found an easy way to accomplish this task. Unfortunately this lead to spending 20 USD more because I used the tool DMG Canvas. With this tool you can create a .dmg installer in no time and signing and notarizing is included!
The program is quite straight forward. Here you see the path to the signed App:
And here you can see the configuration for signing and notarizing
5. Final thoughts
Distributing B4J-jars as signed and notarized app-bundles is a tedious process. But if you know the single building blocks it can be done quickly and easily. I am sure, there will be changes in the future and I hope the process will become easier. But for the moment I know only this cumbersome procedure. Should you know a better way I more than happy to get to know it!
Feel free to contact me: davi
Signing and notarizing:
Tools & Certificates: