8

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
    
Paul Taylor
  • 13,411
  • 42
  • 184
  • 351

4 Answers4

8

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
Dan Gravell
  • 7,855
  • 7
  • 41
  • 64
  • thanks Dan thats hopeful, sounds like first thing I need to do is setup a machine that can run Catalina to build my code and work from there. – Paul Taylor Oct 25 '19 at 09:37
  • Oh yeah - I forgot another step - buy a new Mac Mini (my old one was 2011 and didn't support Catalina)! Also take a look at MacInCloud. – Dan Gravell Oct 25 '19 at 10:56
  • Luckily I already have a newish macmini running catalina, but its just sits under my TV as a video player, not really used for work. – Paul Taylor Oct 25 '19 at 11:58
  • Got it working :) and you say not as bad as i was dreading, I will post by solution as another answer in case useful for others. – Paul Taylor Nov 13 '19 at 14:26
  • @PaulTaylor note my latest edit - might not work forever! – Dan Gravell Nov 14 '19 at 14:54
  • Good ole Apple, Im now looking at their dark mode – Paul Taylor Nov 14 '19 at 14:57
  • Can confirm that I did a build at notarized end of Java with existing method no problem, but I tried a new build today using my existing method and that does indeed now fail to notarize. – Paul Taylor Feb 12 '20 at 18:27
  • typo end of Jan NOT end of java ! – Paul Taylor Feb 12 '20 at 19:18
  • which are ' including a JRE which is built against the latest SDK and with "hardened runtime" support.' – Paul Taylor Feb 12 '20 at 19:19
  • FYI I have found I only need to run codesign once against the app (and once against the dmg) dont need to call it multiple times like yo have to, I dont know if that is because you are using jpackage – Paul Taylor Apr 19 '20 at 07:47
3
  • 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.

Hendrik
  • 5,085
  • 24
  • 56
  • 1
    Thanks -Dhttps.protocols=TLSv1.1,TLSv1.2 fixed this issue https://stackoverflow.com/questions/61478298/since-openjdk-java-11-getting-javax-net-ssl-sslhandshakeexception-received-fata – Paul Taylor Apr 28 '20 at 12:32
  • 1
    FYI [JDK-8223671](https://bugs.openjdk.java.net/browse/JDK-8223671) is now marked as "resolved" as of April 20, 2020, so it should now be possible to notarize with Java 8 as long as you update to a recent release. – ELNJ Jun 03 '20 at 21:30
1

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.

Richard Barber
  • 5,257
  • 2
  • 15
  • 26
  • When I try to start Xcode on old machine it complains You cannot use this version of the application "Xcode" with this version of OSX, I have Xcode 5.0.1 and OS is El Capitan, is there another way ? (if I go to App store then the current version of Xcode is too new for my computer) – Paul Taylor Nov 12 '19 at 20:42
  • Fixed it, just opened Keychain and exported the certificate as .p12 file, then imported it to new computer worked. – Paul Taylor Nov 13 '19 at 09:46
1

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 paultaylor@jthink.net \
 -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)

Paul Taylor
  • 13,411
  • 42
  • 184
  • 351
  • Does the same process works now after 3rd Feb 2020 ? i am using AppBundler to bundle the app and facing code signing the jre packaging. Do you recommend using dmgcanvas instead of .pkg ? – rajneesh Feb 16 '20 at 06:11
  • 1
    No it does not, dmgcanvas was very easy to use upto now, I will update my answer. – Paul Taylor Feb 27 '20 at 14:03
  • Paul, this is very valuable. One question: Did you update your latest version above? If not, most thankful if you do. – carl Feb 17 '21 at 07:05
  • No I am still using 11.0.7 currently – Paul Taylor Feb 17 '21 at 08:19