Paul Taylor
Paul Taylor

Reputation: 13190

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:

Upvotes: 8

Views: 3602

Answers (4)

Paul Taylor
Paul Taylor

Reputation: 13190

Update as of 3rd Feb 2020 Apple have tightened the notarization requirements, answer rewritten.

Note:I required the AdoptJdk Java 11.0.7 JRE, earlier versions did not work for me.

These are my steps

  • Setup new machine (setup src code ectera)
  • Install XCode then go to Preferences:Downloads and select Install Command Line Tools
  • Using KeyChain Export Developer Id Certificate as .p12 format and import into new machine
  • Purchase and install DmgCanvas 3 ($30USD)
  • Renew Apple Developer Account
  • Setup two-step authorisation for my AppleId account (this is partly done on website and partly with iCloud app)
  • Create app specific password (make a note will need for dmgCanvas options)
  • Install AdoptJdk Java 11.0.7 for building
  • Install AdoptJdk Java 11.0.7 JRE for bundling inside app
  • Create songkong.entitlements file
  • Configure build.xml file used by Appbundler InfiniteKind fork to refer directly to the AdoptOpenJDK JRe build
  • Configure build script to sign the bundle created by appbundler, ensuring we use the new signing options required (e.g -runtime, --entitlements, --timestamp)
  • The build script then create a dmg using dmgCanvas, and this additionally signs the dmg and sends it to Apple for notarization

build.xml includes:

<runtime dir="/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jre/Contents/Home"/>

buildosx.sh is

#!/bin/bash
#set -x

cd /Users/paul/code/jthink/songkong
sudo rm -fr /Applications/SongKong.app
mvn -f pommacos.xml -DskipTests=true install
rm -fr target/songkong-6.9
unzip target/songkong-6.9-distribution.zip -d target
ant
export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate"
/usr/bin/codesign --timestamp --options runtime \
--entitlements /Users/paul/code/jthink/songkong/songkong.entitlements \
--sign "Developer ID Application: P Taylor" \
--force --deep --verbose /Applications/SongKong.app
/usr/bin/codesign -vvv --deep --strict /Applications/SongKong.app
spctl -a -t exec -vv /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 -identity "Developer ID Application: P Taylor" \
 -notarizationAppleID [email protected] \
 -notarizationPassword password \
 -notarizationPrimaryBundleID songkong

SongKong entitlements file is:

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

Note:I have also tried this with referring to AdoptJdk Java 11.0.7 JDK build.xml and that also builds without issue (although of course end up witha much larger dmg)

Upvotes: 1

Dan Gravell
Dan Gravell

Reputation: 8230

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

Upvotes: 8

Hendrik
Hendrik

Reputation: 5310

  • 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.

Upvotes: 3

Richard Barber
Richard Barber

Reputation: 6431

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.

Upvotes: 1

Related Questions