13

For some context, I'm using the jpackage utility to try to create a signed DMG file to deliver to my users. The reason I need to sign this DMG is because I would like to notarize the software. By the way, I'm not sure if notarization is possible (yet) using jpackage but I'm trying it anyway.

However, I am having trouble using jpackage's inbuilt code signing options, which is a prerequisite to successful notarization.

I am running jpackage using the options --mac-sign --mac-package-signing-prefix CardrDebate --mac-signing-key-user-name "Developer ID Application: ********** (*******)" (I've redacted the actual developer ID since this is public on StackOverflow).

After creating the jpackage app image, I tested whether the generated code was actually signed by navigating to several of the generated .dylib files and trying codesign -vvv {filename}.dylib, and codesign said that the objectwas not signed at all (NOT that it was incorrectly signed, but that it just wasn't signed at all).

Thus, I believe that my problem is from my (potentially) incorrect usage of jpackage's signing options on macOS. How should I be using these?

Soham
  • 762
  • 1
  • 5
  • 14

4 Answers4

23

I'll go ahead and answer my own question because I ended up figuring out how to sign my application and get it successfully notarized from the Apple notarization service (my product is http://cardr.x10.bz).

  1. Use jpackage's app-image option to generate an unsigned app bundle.

  2. Use an automated bash script to codesign all dylib and executable files inside of the app bundle, using codesign -vvv --options runtime --deep --force --sign "Developer ID Application: ********" <filename>.

  3. This is a multi-step procedure, so I'll just split it up into A/B/C.

3A) Find all jar files within the MyApp.app/Contents/mods/ that contain embedded .dylib files, and extract those files to a specific folder (or write a small program to do this for you). For me, my app relied on JavaFX, so many of the JavaFX libraries contained .dylib files within the jar files. However, if you're just using the default Java libraries, you should be able to skip to step 4, since the default Java libraries don't contain .dylib files. The reason we need to do this step is becuase Apple's notarization service checks these embedded .dylib files for codesigning as well.

3B) Use an automated bash script to codesign all dylib files that you just extracted, using codesign -vvv --options runtime --deep --force --sign "Developer ID Application: ********" <filename>.

3C) Add each of signed .dylib files back into their respective jar files to replace the original unsigned embedded .dylib files. Here's a command that may come in handy: jar uf <path to jar file> <path to dylib file>. Keep note that the second path specified, the path to the dylib file, should also be the dylib's relative location within the archive. Take a look here for more details - https://docs.oracle.com/javase/tutorial/deployment/jar/update.html.

  1. Now that you've signed each of the executable files and dylib files within the .app, it's time to sign the .app itself. Run codesign -vvv --force --sign "Developer ID Application: ********" MyApp.app.

  2. Now that you have signed the .app, you need to run jpackage on the app bundle to create either a DMG or a PKG out of it. Feel free to use the jpackage mac signing features, which will sign the outer DMG/PKG. Take note that the property --mac-signing-key-user-name "My Developer Account Name (*******)" should NOT include the "Developer ID Application/Installer" part of the certificate.

  3. Finally, you have created a signed PKG/DMG ready for notarization. Use xcrun altool --notarize-app --username <apple-id> --password <app-specific-password> <MyApp.dmg or MyApp.pkg>. Wait for notarization to complete and make sure it is approved.

  4. If notarization succeeded (it should), you can staple your app's ticket to the PKG installer using xcrun stapler staple MyApp.pkg.

Hope this helps!

Soham
  • 762
  • 1
  • 5
  • 14
  • I've followed a similar step to yours. However, I need to add `--options runtime` when signing the executable (the dylib doesn't need this flag) otherwise I will get an error saying that "the executable does not have the hardened runtime enabled" from the notarization service. The problem is that the app can't be run after I signed the outer .app with `--options runtime` flag. However, when I omit this flag for the outer app the app can be run without any problem. Do you have any idea regarding this issue? Thank you. – Nuntipat Narkthong Apr 12 '20 at 07:49
  • Thanks for your comment. I've updated the post to include --options runtime; I too had to use this, but I simply forgot to include it in the post. I can't remember what command I used to sign the .app, but I'll check back when I have access to a Mac and let you know. On a side note, if this post helped you, could you please upvote it so that more people can see it? Thanks! – Soham Apr 12 '20 at 20:02
  • @NuntipatNarkthong I didn't use --options runtime when signing the app. I just used codesign -vvv --force --sign MyApp.app. – Soham Apr 17 '20 at 13:55
  • 1
    Apparently the jdk15ea fixed this bug https://bugs.openjdk.java.net/browse/JDK-8237490, but I still have problems with it. Notarization services still shows an dylib which is not signed correctly `Contents/runtime/Contents/MacOS/libjli.dylib`. Can anyone confirm this? – Christoph S Aug 16 '20 at 18:00
  • I also get an error with libjli.dylib - "The signature of the binary is invalid." – Erel May 12 '21 at 09:56
4

FYI - Delved into this problem in JDK 14.0.1 and wanted to share the knowledge as another interim solution until jpackage works correctly.

In the JDK 14 source path: src/jdk.incubator.jpackage/macosx/classes/jdk/incubator/jpackage/internal

the file MacAppBundler.java contains these lines (81 & 82): "Developer ID Application: " + SIGNING_KEY_USER.fetchFrom(params),

where SIGNING_KEY_USER picks up the value of the --mac-signing-key-user-name parameter from the command line.

With these lines using jpackage to sign the DMG always failed. (The 'Developer ID Application: ' did not match the name of my certificate.)

CHANGED these lines to drop the "Developer ID Application: " and the following '+' sign. On invoking jpackage used the full name of the certificate as the parameter value:

--mac-signing-key-user-name "3rd Party Mac Developer Application: John Smith (ABCDEFGHIJ)"

and jpackage will now (apparently) build and sign the DMG. Have not actually attempted to submit this to the Apple Store, so this may still be incomplete.

Interestingly, the MacAppStoreBundler.java source does contain the correct "3rd Party Mac Developer Application: " and "3rd Party Mac Developer Installer: " prefix strings so suspect that jpackager is actually invoking the wrong methods -- but have not yet sorted that out. Possibly jpackage needs to have some additional parameters to specify exactly what should be done (but you would think that '--type dmg' would invoke the correct logic).

Basic (clumsy) steps to reproduce:

  • Download source from https://hg.openjdk.java.net/jdk (picked jdk14, commit 6c954123ee8d).
  • Download and unpack the .zip (or .gz or .bz2) into a working directory
  • Use any text editor to follow the path from 'src' and change MacAppBundler.java as noted above.
  • Open terminal window, and cd into the 'src' directory
  • run 'make all' to compile the entire JDK 14
  • run src/build/macosx-x86_64-server-release/images/jdk/bin/jpackage ...parameters...
JDC
  • 49
  • 6
3

I just want to update on the process. I have a JavaFX 11 app using Java 16 in the build process. The described approach of Soham works. However, here some points I found:

  1. With Java 16 you do not need the "--deep" option anymore to sign the app image (it is also not recommended for deployment).
  2. I needed the entitlement file for signing, i.e. the --entitlement option for signing. Otherwise the app does not start after signing.
  3. The installer can be signed using the jpackage sign parameters.
  4. Uploading the installer as zip for notarization works as expected.
Shark
  • 31
  • 1
3

Another update: With JavaFX/JDK 17.0.2, I was able to build a .dmg file as followed. This file can then be uploaded to a webserver, and downloaded with a webbrowser:

# no entitlements required.
# no further .dylib or .jar signing required.
jpackage --type dmg --mac-sign --mac-signing-key-user-name "YOUR_DEVELOPER_APPLICATION_ID" [...]

# .dmg signing is required for notarization.
codesign --timestamp -s "YOUR_DEVELOPER_APPLICATION_ID" "DMG_NAME"

# recommended notarization steps with verification
xcrun notarytool submit "DMG_NAME" --wait --keychain-profile "YOUR_PROFILE"
xcrun stapler staple "DMG_NAME"
spctl -a -t open --context context:primary-signature -vv "DMG_NAME"
user27772
  • 522
  • 1
  • 4
  • 18
  • Does anyone have issues with jdk19 as well? JDK17 worked fine but jdk19 does not – Christoph S Feb 17 '23 at 19:12
  • I would love to know what else you have in your jpackage command. The rest of my line causes error messages containing weird stuff like --remove-signature and jspawnhelper which make no sense to me :-( – i2B Feb 25 '23 at 19:07
  • 1
    Apart from _--type_, _--mac-sign_ and _--mac-signing-key-user-name_ I used the following parameters (with according values): _--runtime-image_, _--icon_, _--name_, _--app-version_, _--vendor_, _--mac-package-identifier_, _--mac-package-name_, _--module_, _--verbose_ and multiple _--java-options_. – user27772 Feb 27 '23 at 07:36