posts
How to Notarize a Mac Application with Liberica JDK using javapackager

How to Notarize a Mac Application with Liberica JDK using javapackager

Jun 10, 2020
Aleksei Voitylov
21.9

One of the main global trends in IT is the safety of users and preventing the spread of malicious programs, but at the same time, it greatly complicates the life of developers. There are different approaches to solving the issue associated with software security. The most common is the use of an antivirus tool by scanning a computer for malware. A complementary approach was taken by Apple, which introduced the Gatekeeper software to force code signing and check downloaded applications before allowing them to run.

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:

  1. 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
    
    
  2. 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:

  1. 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
    
    
  2. Unlock the key chain:

         security unlock-keychain -p ${YOUR_PASSWORD}
    
    
  3. Create an entitlements file for signing the library (we will use the same entitlements as is used in Liberica JDK, see below).

  4. Sign the library:

         codesign -o runtime -s "${IDENTITY}" \
    
             --prefix me.coley.recaf. \
    
             --entitlements ./entitlements \
    
             -vvvv ./com/sun/jna/darwin/libjnidispatch.jnilib
    
    
  5. 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.

Subcribe to our newsletter

figure

Read the industry news, receive solutions to your problems, and find the ways to save money.

Further reading