The trend is moving towards deanonymization, the violation of anonymity when customers of compromised software have their personal data leaked. The need for notarization arises as a counter to this negative trend by giving users the confidence that a software package was created by an identified developer and checked for malware.
Regarding security, an example is Windows Defender, which applies a signing code for verifying the compliance of a program to security requirements.
Websites use SSL (Security Sockets Layer) certificates for the purpose of ensuring privacy, authentication, and data integrity in online communications. Apple, on the other hand, introduced notarization infrastructure.
Regardless of who is notarizing software, the fact remains that it is an essential, albeit burdensome element of software development imposed by Apple as a requirement now. But there are ways to simplify and even ameliorate the process of notarization for applications in Java and JavaFX that are provided by distributors like Liberica JDK, thus making it a routine procedure, rather than an unpleasant allocation of time and resources. The main advantage of resorting to Liberica JDK is ensuring that applications bundled with the runtime as a single package will be able to pass the notarization procedure smoother and with greater ease.
What is notarization? Why is it necessary?
Notarization gives users more confidence that the Developer ID-signed software being distributed has been checked by Apple for malicious components. Notarization is not an App Review service, as the Apple notary service is an automated system that scans software for malicious content, checks for code-signing issues, and returns the results to the user quickly. If there are no issues, the notary service generates a ticket for the user to staple to their software. The notary service also publishes that ticket online where Gatekeeper can find it. When the user first installs or runs any new software, the presence of a ticket, either online or attached to the executable file, tells Gatekeeper that Apple notarized the software. Gatekeeper then places descriptive information in the initial launch dialog to help the user make an informed choice about whether to launch the app. Now, since the procedure has become mandatory, beginning with macOS Catalina 10.15, it is impossible to launch a non-notarized application.
Liberica JDK is a notarized product. What will that give its customers and users?
Liberica JDK is an alternative to Java and acts as the basis for application development and launching Java SE applications. As an officially recognized and certified application, Liberica JDK has gone through all the steps of notarization and provides full notarization compliance of its software. Liberica JDK binaries have already been notarized for a year since last summer. OpenJDK notarization is not an obvious process and requires a deep signing of all binaries and modules. And Liberica successfully passed the notarization procedure. In order to create an application and have it notarized, users need to refer to already notarized software. This is why using Liberica JDK is one of the easiest and most straightforward means of developing applications that will be recognized and notarized by the Apple notary service. By referring to the notarized Liberica JDK package as an alternative to Java, application developers will be able to receive a shortcut in having their software verified in the future. It is quite easy with Liberica JDK having a special build that facilitates the delivery of app bundles.
The Javapackager element
An important element of the application notarization procedure and an integral part of the Liberica JDK package. The Java Packager tool already comes with the JDK and allows packaging Java applications from the command line, thus being an alternative to other third-party tools. It is important to note that the Java Packager does not automatically produce a Jar file. Many other packager formats are available, including native executable ones tailored for various platforms. Liberica JDK’s Javapackager is ultimately applied during the build process, to create applications that would get easily notarized. Alternatively, it may be used as a standalone tool.
The javapackager tool gives us several options:
- Create a macOS application image (for testing purposes)
- Create a notarized DMG image (suitable for distribution to end users)
- Create a notarized PKG installer (suitable for distribution to end users)
In this article, we will demonstrate how to perform all three operations. But, if you are uncertain or simply do not want to do them on your own, contact our support team. Click the button below, provide us with your details, and you will get advice from expert architects, free of charge.
What if you need to notarize your Java or JavaFX application? A guide
Notarizing an application with the Liberica JDK package and the Javapackager is a straightforward and easy process. Remember to follow the steps thoroughly and use an unsigned copy of Liberica JDK. The resulting application will be notarized and have the necessary credentials to comply with the requirements set by Apple. To get the aforementioned unsigned copy of Liberica JDK, click the button below, leave your details, and our support team will gladly help you.
If you want to make an app using Liberica JDK and Java Packager, you should walk through several stages: create a jar file containing your application, generate a bundle and notarize it.
For the notarization to be successful, it is necessary to get a special build of Liberica JDK 8u252 for macOS, which allows creating a signed application that will be notarized. In this example, it is called bellsoft-jdk8u252+9-macos-amd64-full-nosign.zip. We use a special unsigned Liberica JDK 8u252 because the javapackager cannot re-sign already signed JDK files.
Extract the unsigned Liberica JDK 8u252 bundle:
unzip ./bellsoft-jdk8u252+9-macos-amd64-full-nosign.zip
Also, before creating notarized bundles, do not forget to unlock the developer key:
security unlock-keychain -p "${YOUR_PASSWORD}"
We will next create installer bundles and illustrate this step with the Recaf app.
Building an application JAR file
Recaf version 2.0.0 will serve as an example since it has a better architecture to work as a standalone macOS application. First, we should build a Recaf jar file, create an application bundle and notarize it. The notary service verifies the app’s content; it goes through a jar file and checks that all native libraries in an executable file are signed. So, our job here is to create a Recaf file, check if it has a native part and sign it.
Clone Recaf to your workspace:
git clone https://github.com/Col-E/Recaf.git
Check out the Recaf 2 branch:
git checkout 2.0.0-redesign.10
Compile and package it using the build script or with
mvn clean package
If your build is successful, the resulting jar file will be placed in the target directory and called recaf-2.0.0-J8-jar-with-dependencies.jar.
Java Packager takes the application main class as one of its inputs. If the application’s main class name is unknown, you can easily find it out:
-
Type in the following command to extract the manifest from the jar file:
./jdk8u252.jdk-full-nosign/Contents/Home/bin/jar \ xf ./recaf-2.0.0-J8-jar-with-dependencies.jar \ META-INF/MANIFEST.MF
-
Inspect the extracted
MANIFEST.MF
to find the main class:grep Main-Class META-INF/MANIFEST.MF
The output with the main class name for Recaf is:
Main-Class: me.coley.recaf.Recaf
Signing native libraries within the application
Without signing all native libraries within an application, it will not get notarized. If your app has no native libraries, skip this step. Otherwise, you should expect a notarization error: The Apple notary service will respond with an error message indicating the presence of an unsigned library.
Our sample application, Recaf, does contain a native library libjnidispatch.jnilib
inside its jar file. This means we need to sign the native library file in Recaf’s jar for further notarization. Follow these steps:
-
Extract the library from the jar file:
./jdk8u252.jdk-full-nosign/Contents/Home/bin/jar \ xf ./recaf-2.0.0-J8-jar-with-dependencies.jar \ com/sun/jna/darwin/libjnidispatch.jnilib
-
Unlock the key chain:
security unlock-keychain -p ${YOUR_PASSWORD}
-
Create an entitlements file for signing the library (we will use the same entitlements as is used in Liberica JDK, see below).
-
Sign the library:
codesign -o runtime -s "${IDENTITY}" \ --prefix me.coley.recaf. \ --entitlements ./entitlements \ -vvvv ./com/sun/jna/darwin/libjnidispatch.jnilib
-
Add the signed library to Recaf’s jar file:
./jdk8u252.jdk-full-nosign/Contents/Home/bin/jar \ uf recaf-2.0.0-J8-jar-with-dependencies.jar \ com/sun/jna/darwin/libjnidispatch.jnilib
Now you can use the Recaf jar to create and notarize your macOS application with bundled Liberica JDK.
Creating a Mac application image with bundled Liberica JDK
Liberica JDK's javapackager signs the application, adds the necessary entitlements, secures timestamp and the hardened runtime while creating native macOS images.
Should you need help on the options, type in the inline help command:
./jdk8u252.jdk-full-nosign/Contents/Home/bin/javapackager \
-help <Native image type - mac.app, pkg etc.> \
<-verbose>
Here are all the bundler parameters for Mac Application Image (mac.app):
name - App Name - String
arguments - Command Line Arguments - List
mac.bundle-id-signing-prefix - Bundle Signing Prefix - String
classpath - Main Jar Classpath - String
mac.signing-key-developer-id-app - Apple Developer ID Application Signing Key - String
icon.icns - .icns Icon - File
jvmOptions - JVM Options - List
jvmProperties - JVM System Properties - Map
mac.category - Category - String
mac.CFBundleIdentifier - CFBundleIdentifier - String
mac.CFBundleName - CFBundleName - String
mac.CFBundleVersion - CFBundleVersion - String
runtime - JRE - RelativeFileSet
applicationClass - Main Class - String
mainJar - Main Jar - RelativeFileSet
preferencesID - Preferences ID - String
userJvmOptions - User JVM Options - Map
appVersion - Version – String
The next step is to create a Mac native application image with the following command:
./jdk8u252.jdk-full-nosign/Contents/Home/bin/javapackager -deploy \
-native image \
-name Recaf \
-outdir res_image \
-srcfiles ./recaf-2.0.0-J8-jar-with-dependencies.jar \
-outfile recaf \
-appclass me.coley.recaf.Recaf \
-Bruntime=./jdk8u252.jdk-full-nosign/Contents/Home/ \
-BappVersion=2.0.0-liber
Besides, you may pass your entitlements file using the -Bmac.entitlements=\<entitlements file path\>
option. If the entitlement file is not specified, the following entitlements are used:
<?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">
<plist version="1.0">
<dict>
<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.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>
The resulting image will be created in the res_image/bundles/Recaf.app directory.
You can ensure that your application is signed and has the necessary properties/options with the command:
codesign -dv --entitlements :- ./res_image/bundles/Recaf.app/
It should produce the following output:
Executable=***/res_image/bundles/Recaf.app/Contents/MacOS/Recaf
Identifier=me.coley.recaf
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=378 flags=0x10000(runtime) hashes=3+5 location=embedded
Signature size=8998
Timestamp=***********************
Info.plist entries=15
TeamIdentifier=8LBATW8FZA
Runtime Version=10.14.0
Sealed Resources version=2 rules=13 files=5
Internal requirements count=1 size=176
<?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">
<plist version="1.0">
<dict>
<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.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>
Given that application images are most suitable for testing and not typically used directly in distribution, we will now focus on creating DMG or PKG bundles tailored for end users.
Creating a DMG bundle
Choosing between DMGs and PKGs depends on your preference. While these two are perfect for end-user distribution, they possess slightly different qualities. Some developers and vendors ship software in both formats, or go for wrapping a PKG inside a DMG. DMG is effectively a disk image the end user can click on and install an application in the same way as when inserting a CD.
Issue the following command to create a DMG image with javapackager:
./jdk8u252.jdk-full-nosign/Contents/Home/bin/javapackager -deploy \
-native dmg \
-name Recaf \
-outdir res_dmg \
-srcfiles ./recaf-2.0.0-J8-jar-with-dependencies.jar \
-outfile recaf \
-appclass me.coley.recaf.Recaf \
-Bruntime=./jdk8u252.jdk-full-nosign/Contents/Home/ \
-BappVersion=2.0.0-liber
The ready DMG image will be placed in ./res_dmg/bundles/Recaf-2.0.0-liber.dmg.
Then submit the newly made DMG bundle to Apple for a standard procedure and get it notarized. Apple provides a set of tools for notarizing bundles. The two important ones are altool
(to interact with the notary service) and stapler (to work with notarization results and staple the bundle afterward).
Send the DMG bundle to the Apple notary service by typing in the command:
xcrun altool --notarize-app \
-primary-bundle-id com.bell-sw.recaf.0 \
-username ${USERNAME} \
-password '${YOUR_PASSWORD}' \
-file ./res_dmg/bundles/Recaf-2.0.0-liber.dmg
You should get the response:
2020-04-27 08:43:58.152 altool[47259:19144203] No errors uploading 'res_dmg/bundles/Recaf-2.0.0-liber.dmg'.
RequestUUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
At this point, you need to wait for a response from the notary service and check the notarization request status occasionally using the provided request UUID:
xcrun altool --notarization-info ${RequestUUID} \
-u ${USERNAME} \
-p '${YOUR_PASSWORD}'
Please note that you will have to wait a while for the notarization status. If requested right away, instead of the status, an error will show up saying the request UUID is not found. This is what you will see when the service processes your bundle:
*********************** altool[47696:19145453] No errors getting notarization info.
RequestUUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Date: ***
Status: in progress
LogFileURL: (null)
Once the bundle is verified, you will get the ‘success’ status, and the package becomes approved:
*********************** altool[88539:21766482] No errors getting notarization info.
RequestUUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Date: ***
Status: success
LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets/*********
Status Code: 0
Status Message: Package Approved
By following the LogFileURL link, you will get a formatted JSON response:
{
"logFormatVersion": 1,
"jobId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"status": "Accepted",
"statusSummary": "Ready for distribution",
"statusCode": 0,
"archiveFilename": "Recaf-2.0.0-liber.dmg",
"uploadDate": "********************",
"sha256": "3d742fbfbbf6252a6d35eb23114b387934dac1c0be423b357c0b257026cd1fba",
"ticketContents": [
{
"path": "Recaf-2.0.0-liber.dmg",
"digestAlgorithm": "SHA-256",
"cdhash": "56e2fa7437a638721fed896a24880607ffca619d"
},
{
"path": "Recaf-2.0.0-liber.dmg/Recaf.app",
"digestAlgorithm": "SHA-256",
"cdhash": "8739a51515aded06148e434b735ddf7147d4821a",
"arch": "x86_64"
},
...
],
"issues": null
}
Now that the Apple notary service has approved the bundle and generated a special ticket, staple the bundle with the command:
xcrun stapler staple ./res_dmg/bundles/Recaf-2.0.0-liber.dmg
The output of the ticket stapling command should look like:
Processing: *****/res_dmg/bundles/Recaf-2.0.0-liber.dmg
Processing: *****/res_dmg/bundles/Recaf-2.0.0-liber.dmg
The staple and validate actions worked!
Validating the DMG bundle
You can also validate your bundle separately with the command:
xcrun stapler validate ./res_dmg/bundles/Recaf-2.0.0-liber.dmg
This should produce the following output:
Processing: *****/res_dmg/bundles/Recaf-2.0.0-liber.dmg
The validate action worked!
If the validation is successful, you should be able to install the application!
Creating a PKG bundle
As we said before, it is required to choose either a DMG or PKG file to create at this stage. You may opt for PKG as this installer format provides sophisticated means to build an application installation experience.
The instructions for making a notarized PKG file using the javapackager are very similar to those for the DMG one.
Type in this command to generate a PKG bundle for Recaf:
./jdk8u252.jdk-full-nosign/Contents/Home/bin/javapackager -deploy \
-native pkg \
-name Recaf \
-outdir res_pkg \
-srcfiles ./recaf-2.0.0-J8-jar-with-dependencies.jar \
-outfile recaf \
-appclass me.coley.recaf.Recaf \
-Bruntime=./jdk8u252.jdk-full-nosign/Contents/Home/ \
-BappVersion=2.0.0-liber
Then you should notarize it by sending it to the Apple notary service:
xcrun altool --notarize-app \
-primary-bundle-id com.bell-sw.recaf.0 \
-username ${USERNAME} \
-password '${YOUR_PASSWORD}' \
--file ./res_pkg/bundles/Recaf-2.0.0-liber.pkg
The output will look like this:
*********************** altool[30652:19480464] No errors uploading 'res_pkg/bundles/Recaf-2.0.0-liber.pkg'.
RequestUUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Wait a little and check the notarization results:
xcrun altool --notarization-info ${RequestUUID} \
-u ${USERNAME} \
-p '${YOUR_PASSWORD}'
Please note that it may take some time for the Apple notary service to process the request.
When the notarization is complete, this command will return:
*********************** altool[30708:19481150] No errors getting notarization info.
RequestUUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Date: ***********************
Status: success
LogFileURL: https://osxapps-ssl.itunes.apple.com/*****
Status Code: 0
Status Message: Package Approved
You may follow the LogFileURL link to see the notarization details for your bundle:
{
"logFormatVersion": 1,
"jobId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"status": "Accepted",
"statusSummary": "Ready for distribution",
"statusCode": 0,
"archiveFilename": "Recaf-2.0.0-liber.pkg",
"uploadDate": "***********************",
"sha256": "cf6480a8feb54236a9e0a2894a85c130b373a173bc270e661f111e2df4b65e4c",
"ticketContents": [
{
"path": "Recaf-2.0.0-liber.pkg",
"digestAlgorithm": "SHA-1",
"cdhash": "22d41fe89a8af1bcd7e594eab94dce942f822874"
},
{
"path": "Recaf-2.0.0-liber.pkg/Recaf-app.pkg Contents/Payload/Applications/Recaf.app/Contents/MacOS/libpackager.dylib",
"digestAlgorithm": "SHA-256",
"cdhash": "81ec29cd1ed5c30b1ac0f9aca785e8389d3b0c16",
"arch": "x86_64"
},
...
],
"issues": null
}
Having done that, staple the generated ticket to the application:
xcrun stapler staple ./res_pkg/bundles/Recaf-2.0.0-liber.pkg
This command will produce the following output:
Processing: ***/res_pkg/bundles/Recaf-2.0.0-liber.pkg
Processing: ***/res_pkg/bundles/Recaf-2.0.0-liber.pkg
The staple and validate actions worked!
Validating the PKG bundle
You can check the signature in the PKG file by clicking the lock icon in the top right corner. The popped-up dialog informs of all its details.
Conclusion
Notarizing an application with the Liberica JDK package and the Javapackager is a straightforward and easy process. Remember to follow the steps thoroughly and use an unsigned copy of Liberica JDK. The resulting application will be notarized and have the necessary credentials to comply with the requirements set by Apple. To get the aforementioned unsigned copy of Liberica JDK, click the button below, leave your details, and our support team will gladly help you.