Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Notarize existing Java application for MacOS Catalina

I distribute a Java application for MacOS, it is developer signed but not notarized. Not really sure where to start with this since the documentation is so biased towards creating apps with Xcode that I do not use, but I just want the simplest way to notarize my app and then move on.

Reading the documentation I have a few concerns already:

  • I am currently using Java 8, is it possible to notarize a Java 8 app or do I need to move to Java 11. I would rather not move to Java 11 because it would cause problem on some other platforms I support.

  • My dev Mac machine is an old MacBook Pro, and as such cannot be updated past OSX El Capitan 10.11.6, can I notarize with this machine or not? I do have a more recent machine but it is not setup for development and I have some concerns about transferring the Developer Id certificates to it because setting this up was problematic in first place.

  • I use the AppBundler fork https://github.com/TheInfiniteKind/appbundler/ to package my app

  • This is called by an ant script build file that does the signing etc, we eventually create a dmg using dmgCanvas

  • I post the ant script below, hoping someone can start me of with the basic steps

    #!/bin/bash
    #set -x
    
    cd /Users/paul/code/jthink/songkong/src/main/scripts
    hiutil -C  -fapplehelpbook/SongKongHelp/SongKongHelp.helpindex applehelpbook/SongKongHelp/
    cd /Users/paul/code/jthink/songkong
    rm -fr /Applications/SongKong.app
    mvn clean
    mvn -DskipTests=true install
    rm -fr target/songkong-6.6
    unzip target/songkong-6.6-distribution.zip -d target
    ant
    sudo cp -r target/songkong-6.6/applehelpbook/SongKongHelp /Applications/SongKong.app/Contents/Resources
    rm /Applications/SongKong.app/Contents/PlugIns/jdk1.8.0_192.jdk/Contents/MacOS/libjli.dylib
    cp /Applications/SongKong.app/Contents/PlugIns/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jli/libjli.dylib /Applications/SongKong.app/Contents/PlugIns/jdk1.8.0_192.jdk/Contents/MacOS
    export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate"
    
    /usr/bin/codesign --sign "Developer ID Application: P Taylor" --force --deep --verbose /Applications/SongKong.app
    /usr/bin/codesign --verify --deep  --verbose /Applications/SongKong.app
    
    cd /Users/paul/code/jthink/SongKong
    /usr/local/bin/dmgcanvas /Users/paul/code/jthink/SongKong/dmgCanvas_songkong.dmgCanvas /Users/paul/songkong-osx.dmg -v SongKong
    
like image 296
Paul Taylor Avatar asked Oct 24 '19 20:10

Paul Taylor


3 Answers

Edit 12/2/2020 - there have been a lot of changes because Apple have slowly tightened the requirements for notarization. From Feb 3rd they seem to have hit the final stage, which means your app has to meet much higher requirements, including a JRE which is built against the latest SDK and with "hardened runtime" support.

So I've stripped much of the old discussion.

My first issue was setting up - you need an active Developer Programme account with Apple ID (which is easy) but then, when you follow the instructions to add a password to the keychain, use the App specific password. You also need to enable two factor auth for your Apple ID account.

Once you work out the command line calls it's pretty easy to automate in a build script. I have used jpackage to create the app and the DMG but beware - currently its approach to signing the app does not work.

In terms of scripting, here's what I'm doing to code sign the app suitable for notarization (this assumes a .app is already created):

% security unlock-keychain -p passwordhere codesigning.keychain
% find my-app.app -type f \
  -not -path "*/Contents/runtime/*" \
  -not -path "*/Contents/MacOS/my-app" \
  -not -path "*libapplauncher.dylib" \
  -exec codesign --timestamp --entitlements /tmp/bliss.entitlements -s "XXX" --prefix com.myapp. --options runtime -v --keychain /path/to/codesigning.keychain {} \;

% find my-app.app/Contents/runtime -type f \
  -not -path "*/legal/*" \
  -not -path "*/man/*" \
  -exec codesign -f --timestamp --entitlements /tmp/bliss.entitlements -s "XXX" --prefix com.myapp. --options runtime -v --keychain /path/to/codesigning.keychain {} \;

% codesign -f --timestamp --entitlements /tmp/bliss.entitlements -s "XXX" --prefix com.myapp. --options runtime -v --keychain /path/to/codesigning.keychain my-app.app/Contents/runtime

% codesign -f --timestamp --entitlements /tmp/bliss.entitlements -s "XXX" --prefix com.myapp. --options runtime -v --keychain /path/to/codesigning.keychain my-app.app

The entitlements should be:

<?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>

All tests work:

% codesign -vvv --deep --strict my-app.app/Contents/runtime 
my-app.app/Contents/runtime: valid on disk
my-app.app/Contents/runtime: satisfies its Designated Requirement
% codesign -vvv --deep --strict my-app.app/                
--prepared:/private/tmp/my-app.app/Contents/MacOS/libapplauncher.dylib
--validated:/private/tmp/my-app.app/Contents/MacOS/libapplauncher.dylib
my-app.app/: valid on disk
my-app.app/: satisfies its Designated Requirement
% spctl -a -t exec -vv my-app.app          
my-app.app: accepted
source=Developer ID
origin=XXX

At this point you should also try running your app - the code signing process can break things.

From here, you can create a DMG (again, I use jpackage) and this should pass notarization.

In summary:

  1. Build the app in the correct structure
  2. Create an entitlements file
  3. Code sign your code
  4. Code sign the files inside bundled runtime, forcing the signature
  5. Code sign the bundled runtime itself
  6. Code sign your app file
  7. Package into a DMG
  8. Notarize it
  9. Ship it
like image 168
Dan Gravell Avatar answered Oct 19 '22 05:10

Dan Gravell


  • AFAIK, you need Java 11 (see JDK-8223671), however, recently I was told that Java 8 may also work. I haven't tried this.

  • JDK-8223671 contains some useful information. Specifically, you need to add entitlements to your code sign call:

codesign --entitlements java.entitlements --options runtime --deep -vvv -f --sign "Developer ID Application: Bla Bla (XXXX)" YourApp.app

A working sample java.entitlements file could look like this:

<?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> 
  • Bundling a jlink generated runtime is a pain, because it contains sym links (which aren't allowed during signing) and also a legal folder that contains folder names like java.xml (with a .). codesign is unfortunately a little dumb and believes such folders are unrecognized bundles and aborts. Therefore you should rename those files/folders and resolve any sim links before jlinking.

  • If you use jlink, make sure you add needed service providers, e.g., jdk.crypto.ec for HTTPS. Also note that AFAIK, in Java 11 TLSv1.3 is at least partially broken (upload of large files) and you should disable it, e.g. with -Dhttps.protocols=TLSv1.1,TLSv1.2.

  • If you use the AppBundler fork, you will need to make sure it also adheres to Apple's guidelines, i.e., is linked against macOS 10.9. I believe by default AppBundler links against 10.7, but changing it is simple.

  • If you are using Java 11 or later, make sure that you bundle libjli.dylib in /Contents/PlugIns/JAVA_PLUGIN_NAME/Contents/Home/lib/jli/libjli.dylib. Apparently that's needed by the launcher and may not be bundled by default.

  • Some of your other questions are answered in Apple's guidelines:

Notarization requires Xcode 10 or later. Building a new app for notarization requires macOS 10.13.6 or later. Stapling an app requires macOS 10.12 or later.

like image 39
Hendrik Avatar answered Oct 19 '22 05:10

Hendrik


To notarize requires Xcode 10, and to staple requires at least Sierra.

“Notarization requires Xcode 10 or later. Building a new app for notarization requires macOS 10.13.6 or later. Stapling an app requires macOS 10.12 or later.” https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution

As for transferring dev certs, let Xcode handle this task by exporting your profile on the old machine and importing it on the new one.

like image 36
Richard Barber Avatar answered Oct 19 '22 05:10

Richard Barber