12

I currently distribute a Java application, packaged and signed using pkgbuild on macOS.

Recently, Apple warns developers:

"In an upcoming release of macOS, Gatekeeper will require Developer ID–signed software to be notarized by Apple."

Upon reading the notarizing docs, apple warns developers:

"You must enable hardened runtime for your app to be notarized by Apple."

Which goes into some details about how to toggle these settings on within Xcode. But what about apps not developed with Xcode? Xamarin/Mono have some back and forth about how they're tackling this but the commits so far seem to focus on Mojave detection and C/C++. What about Java apps or apps that don't distribute any C/C++/Objective-C compiled code? How does a project get "notarized" so that it doesn't get blocked by a future macOS update?

Possibly related: How to codesign and enable the hardened runtime for a 3rd-party CLI on Xcode?

tresf
  • 7,103
  • 6
  • 40
  • 101
  • Hi, I found the solution finally. Check mine: https://stackoverflow.com/questions/52905940 – SuperBerry Oct 21 '18 at 13:12
  • 1
    @superberry thanks! How does this fullfil the Apple hardening requirements which seem to be related to the way the software is complied? For example, what if I distribute a pure Python or Ruby app (no compiled code)? How then would the project be hardened so that Apple will notarize? Yours doesn't seem to have the information Apple requires for notarization. See here: https://help.apple.com/xcode/mac/current/en.lproj/Art/ca_enablehardenedruntime.png – tresf Oct 21 '18 at 13:34
  • Hi, if you use Xcode, you could find the "hardened runtime" switch in the "Build Settings"->"All". If you mean the command line tool, I just use the codesign command in the answer of the above link to force it runs as "runtime". Hope this could work for you. – SuperBerry Oct 21 '18 at 13:37
  • 1
    I don't use Xcode, the app is Java. Your example just appears to apply code signing to an executable, not fullfil the hardening requirements, no? – tresf Oct 21 '18 at 13:39
  • Per our conversation on another thread, @superberry claims the option `--options runtime` handles this. This is a good start. My `.pkg` has shell scripts for install and launch but just one big fat jar file. Some components are `.dylib`/`.so` with JNI (Java Native Interface) bundled. Java (already installed on the system) will run these. I have no idea how to fulfil these new requirements. :/ – tresf Oct 21 '18 at 13:47
  • After reading https://twitter.com/rosyna/status/1004418504408252416 I've submitted our `.pkg` for evaluation -- making no changes -- using `xcrun altool --eval-app --primary-bundle-id -u -f ` and after a minute or so, it returned `RequestUUID = a1b2c3d4e5-a1b2-a1b2-a1b2-a1b2c3d4e5f6` which apparently I have to monitor using `xcrun altool --eval-info a1b2c3d4e5-a1b2-a1b2-a1b2-a1b2c3d4e5f6 -u ` or via email. Note, the User Agreement had changed on developer.apple.com, so I signed in and agreed before attempting. – tresf Oct 21 '18 at 14:16
  • I decided to submit for notarization to learn more. Trial and error so far is here: https://github.com/qzind/tray/issues/372#issuecomment-431674246 – tresf Oct 21 '18 at 14:38
  • 1
    I got the notarized successful message via email... – SuperBerry Oct 21 '18 at 14:48
  • My notarization request was declined. The JSON log (available using the `--eval-info` command mentioned above) explains why. Compiled objects (`.dylib`, `.jnilib`, NOT scripts, NOT JAR files) that come with some 3rd-party Java libraries we bundle aren't signed. We'll modify our build system to sign these and see how much further we get. My (speculative) anticipation is that Apple (currently) only cares about hardening compiled objects, so we'll start there. – tresf Oct 21 '18 at 14:57
  • I've managed to get the notarization to pass. The steps were quite complex, they're in a dedicated answer below. – tresf Nov 28 '18 at 21:16
  • OpenJDK mail thread: http://mail.openjdk.java.net/pipermail/jdk-dev/2018-December/002460.html – ZhekaKozlov Dec 19 '18 at 15:02

3 Answers3

19

I'm answering this question in regards to a Java project that requires notarization. With slight modifications, the answer should work for other types of projects (python, powershell, node) as well.

Note: At the time of posting this, Apple's notarization command allowed the below procedure to work however as notarization and security becomes more common and more strictly enforced it is inevitable that Apple will change and improve hardening requirements and procedures. Please edit, comment or re-answer as needed.

Code Signing

  • For a vanilla Java app (.pkg or .app containing scripts, jars), the notarization should pass. During notarization, Apple will extract the .jar and look for native libraries. If it finds any that aren't signed, it'll be rejected. If it doesn't, you're OK. Instructions for notarization using xcrun are further below.
  • For a Java app which contains native calls (e.g. JNI) to bundled libraries (.dylib, .jnilib) each bundled library must be signed using an "Application" (e.g. developerID_application.cer) certificate.

    • Certificates, Identifiers & Profiles, (Click "iOS, tvOS, watchOS" dropdown) macOS, Developer ID Application. (may also say "with Kext"). apple developer portal
    • If you don't have this certificate, you'll need to request one using a CSR. In my case, I originally only had a certificate for packaging installers (not codesigning). This process can get tricky especially if you use the same private key for two certificates. Use openssl via command line (instead of the Keychain Access) if you get stuck.
    • Once you obtain the certificate, signing each native library .dylib|.jnilib|.so|bin gets tricky. The general idea is to use codesign command against the native library so that it is signed as you, the developer. The syntax is:

      xargs codesign -s "P6DMU6694X" -v dependency.dylib

      ... where P6DMU6694X is either the unique developer ID or the exact certificate Common Name (either will work).

    • For a .jar file, this can be particularly cumbersome as each package needs to be extracted, signed and then zipped back up.

Notarization

  • Once the native libraries are signed the package must be sent for notarization using xcrun.

    xcrun altool --eval-app --primary-bundle-id <bundle id> -u <iTunes Connect Account> -f <file path>

    Which may look something like this:

    xcrun altool --eval-app --primary-bundle-id com.domain.appname -u john@domain.com -f appname.pkg
  • You will be prompted for your Apple Developer password (NOT the password you use to login to your Mac). Edit: Since dual-factor has been mandated, you'll need to create an app-specific password for this step!

  • After a few minutes, the xcrun command will return a unique ID that can be used to determine if the notarization was approved.

    RequestUUID = a1b2c3d4e5-a1b2-a1b2-a1b2-a1b2c3d4e5f6
  • Periodically check the status of this unique ID to see if it was approved or denied.
    xcrun altool --eval-info a1b2c3d4e5-a1b2-a1b2-a1b2-a1b2c3d4e5f6 -u john@domain.com
  • If denied, they won't directly tell you why, you have to parse the JSON response.

    LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets/...
  • Read the JSON and correct the problems identified. The JSON is minified, you may want to run it through a pretty-formatter. If there are no problems, your app has been notarized and is Ready for distribution.

    
    {
      "logFormatVersion": 1,
      "jobId": "a1b2c3d4e5-a1b2-a1b2-a1b2-a1b2c3d4e5f6",
      "status": "Accepted",
      "statusSummary": "Ready for distribution",
      "statusCode": 0,
      "archiveFilename": "appname.pkg",
      "uploadDate": "2018-10-26T05:41:12Z",
      "sha256": "e2350bda66...",
      "issues" null
    }
    

Stapling

Finally, stapling the build will ensure the package is trusted even when a network connection is not available.

(apple.com) You should also attach the ticket to your software using the stapler tool, so that future distributions include the ticket. This ensures that Gatekeeper can find the ticket even when a network connection isn’t available. To attach a ticket to your app, use the stapler tool:

xcrun stapler staple appname.pkg

Runtime

An additional solution provided by @NaderNader, if bundling the Java runtime along with a .app, additional steps are needed to mark the distribution as a runtime using the --option=runtime flag, where P6DMU6694X is your signing ID:

codesign --force --deep --options=runtime -s "P6DMU6694X" /path/to/My.app
tresf
  • 7,103
  • 6
  • 40
  • 101
  • Apple now requires dual-factor authentication and setting an app-specific password. This can be done via https://appleid.apple.com, Security, Generate a password. – tresf Mar 02 '19 at 19:53
  • 1
    @tobihagemann had recommended an edit `--eval-app` is replaced with `--notarize-app` and that `--eval-info` is replaced with `--notarization-info`. I found these original commands on Twitter so are they deprecated? I'd be happy to update with the new commands if provided supporting documentation. <3 – tresf Sep 09 '19 at 18:04
  • 1
    This answer got me almost there. In my case I bundle my java, jni, and the jre using packr.jar to generate a My.app executable. I was getting "The executable does not have the hardened runtime enabled" failures during Notarization. I resolved this issue by signing My.app using the codesign '--options=runtime'. Full command: codesign --force --deep --options=runtime -s "" /path/to/My.app. Once signed, I was able to create an installer with pkgbuild, sign the installer with productsign, and notarize successfully with altool. – NaderNader Apr 27 '20 at 19:21
  • Added a new "Runtime" section for those bundling the JVM. Note, this varies a bit from the rest of the tutorial as it assumes `.pkg` rather than what I'd assume to be an `.app|.dmg` deliverable, but I've done my best to mention that. I've also not tested this myself, so I'll have to take NaderNader's word for it. :D – tresf Apr 30 '20 at 19:08
5

In addition to tresf's answer above, if your app is sandboxed (and possibly even if not) then the hardened runtime will fail when the JVM is loaded. To work around that you'll need to add some keys to your entitlements when signing. The necessary entitlement entries are below, copied from TAO ZHOU's solution here: https://github.com/TheInfiniteKind/appbundler/issues/39

<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/>
Sean Reilly
  • 748
  • 6
  • 17
  • Can you add some comments to explain how each line pertains to the JVM? For example, `com.apple.security.cs.allow-dyld-environment-variables`, is this line due to a specific use-case where your application provides `DYLD_LIBRARY_PATH` at runtime? If so, can you comment that? `com.apple.security.cs.allow-jit` is this due to non-compiled Java code that's being interpreted at runtime? What use-cases triggered the need for this? Any elaborate on each item is greatly appreciated. – tresf Apr 17 '19 at 14:31
  • I couldn't say what all of them are for, but I believe that most are to allow the JVM to perform JIT compilation and write to executable memory, which I assume the hardened runtime would normally disallow. It could be that the DYLD environment variables one is only needed by the script-based launcher, but I couldn't say for sure. Our app is not making any runtime changes to the dynamic library path, so I assume that's needed to enable the JVM's startup process. For reference, the console error before adding these lines was "vm_map_enter: curprot cannot be write+execute. failing" – Sean Reilly Apr 19 '19 at 10:28
  • Oh my goodness. Thank you! Was trying all sorts of things and this was the winner. – nevster Jun 30 '20 at 15:02
  • The given link is broken – BrainStorm.exe Oct 06 '20 at 18:25
  • The link is now fixed. Sorry about that, it was broken when we moved from bitbucket to github. – Sean Reilly Oct 07 '20 at 19:09
1

Here is a simple shell script that will codesign the .so, .dylib and .jnilib within a jar file

usage: codesign_jar_script.sh filename.jar

jar tf $1 | grep '\.so\|\.dylib\|\.jnilib'  > filelist.txt

IDENTITY="your_signing_identity"

echo $IDENTITY

while read f
do
    jar xf $1 $f
    codesign --force -s "$IDENTITY" -v $f
    jar uf $1 $f
    rm -rf $f
done < filelist.txt
s6ch13
  • 9
  • 4