使用 jpackage 实用程序时出现此 Mac 应用程序签名错误的原因是什么?

问题描述 投票:0回答:1

我正在尝试使用

jpackage
实用程序将 Java 应用程序打包为 Mac (Sonoma 14.3.1) 的应用程序包。在Mac终端的bash shell中运行,相关命令是

sudo jpackage \
    -i deploy \
    -d deploy-mac \
    -n "MyApp Name" \
    --type app-image \
    --main-class "com.my.class.MyClass" \
    --main-jar "MyApp.jar" \
    --app-version 1.0 \
    --copyright "..." \
    --description "..." \
    --icon "..." \
    --vendor "..." \
    --mac-app-category "public.app-category.education" \
    --verbose \
    --mac-sign \
    --mac-signing-keychain login \
    --mac-signing-key-user-name "XYZ Signing ID"

其中“XYZ 签名 ID”是有效 Apple 开发者 ID 证书的 ID。我目前使用的是 JDK 21。

当我省略最后三个参数(即

--mac-sign
--mac-signing-keychain
--mac-signing-key-user-name
)时,该过程会顺利完成,但这需要大量的事后伏都以确保应用程序运行并被接受苹果的“看门人”。

当包含三个参数时,命令失败并出现神秘错误:

[12:02:04.264] Running /usr/bin/security
[12:02:04.289] Command [PID: 33259]:
    /usr/bin/security find-certificate -c Developer ID Application: XYZ Signing ID -a login
[12:02:04.291] Returned: 0

[12:02:04.291] Running /usr/bin/security
[12:02:04.309] Command [PID: 33260]:
    /usr/bin/security find-certificate -c Developer ID Application: XYZ Signing ID -a -p login
[12:02:04.309] Returned: 0

[12:02:04.312] Running /usr/bin/openssl
[12:02:04.321] Command [PID: 33261]:
    /usr/bin/openssl x509 -noout -subject -in /private/var/folders/_g/dwvgjlm50sl23mp5w7s3cf8c0000gn/T/tempfile17263379665633563715.tmp
[12:02:04.321] Output:
    unable to load certificate
    140704282142656:error:09FFF06C:PEM routines:CRYPTO_internal:no start line:/AppleInternal/Library/BuildRoots/4e1473ee-9f66-11ee-8daf-cedaeb4cabe2/Library/Caches/com.apple.xbs/Sources/libressl/libressl-3.3/crypto/pem/pem_lib.c:694:Expecting: TRUSTED CERTIFICATE
[12:02:04.322] Returned: 1

[12:02:04.322] java.io.IOException: Command [/usr/bin/openssl, x509, -noout, -subject, -in, /private/var/folders/_g/dwvgjlm50sl23mp5w7s3cf8c0000gn/T/tempfile17263379665633563715.tmp] exited with 1 code
    at jdk.jpackage/jdk.jpackage.internal.Executor.executeExpectSuccess(Executor.java:90)
    at jdk.jpackage/jdk.jpackage.internal.IOUtils.exec(IOUtils.java:229)
    ...
[12:02:04.324] jdk.jpackage.internal.PackagerException: Bundler Mac Application Image skipped because of a configuration problem: Signature explicitly requested but no signing certificate found 
Advice to fix: Specify a valid mac-signing-key-user-name and mac-signing-keychain
    at jdk.jpackage/jdk.jpackage.internal.Arguments.generateBundle(Arguments.java:702)
    at jdk.jpackage/jdk.jpackage.internal.Arguments.processArguments(Arguments.java:555)
    ...
[12:02:04.323] No certificate found matching [Developer ID Application: XYZ Signing ID] using keychain [login]

很明显,签名证书存在并且有效(对 /usr/bin/security 的调用均成功),但对

openssl
的调用失败,并在验证证书时出现临时文件解析错误。更糟糕的是,临时文件(可能包含证书数据)在每次调用时都会发生变化,并在
jpackage
中止之前被删除,这意味着无法检查该文件。我尝试过使用
--temp
参数来指定临时目录,但这不起作用(应用程序行为未更改)。

我不知道是什么原因导致此错误或如何诊断它。我怀疑这是

jpackage
中的错误,但我没有使用大多数 Java 应用程序不常见的任何功能。因此,除非每个人尝试为 Mac 构建基于 Java 的应用程序包时遇到此错误,否则我一定做错了什么。

我的问题是:这个错误的原因是什么,有什么简单的修复或解决方法吗?


万一没有简单的解决方法,我在下面提供了我自己的(更详尽的)解决方法作为答案。

java macos deployment code-signing
1个回答
0
投票

解决方法

使用 JDK 21,我能够运行应用程序,并使用 Apple 的公证工具使用以下解决方法成功公证应用程序包。

假设 .JAR 文件是

deploy/MyApp.jar
并且 XCode 已安装。另假设“开发者 ID 应用程序:XYZ 签名 ID (ABCDEFGHIJK)”是在本地(即
login
)钥匙串上注册的有效 Apple 开发者 ID 证书。

首先,运行不带

jpackage
参数的
--mac-sign
,即

sudo jpackage \
    -i deploy \
    -d deploy-mac \
    -n "MyApp Name" \
    --type app-image \
    --main-class "com.my.class.MyClass" \
    --main-jar "MyApp.jar" \
    --app-version 1.0 \
    --copyright "..." \
    --description "..." \
    --icon "..." \
    --vendor "..." \
    --mac-app-category "public.app-category.education" \
    --verbose

这应该创建

deploy-mac/MyApp Name.app
作为应用程序包。

创建文件

resource/mac-entitlements.plist
,内容为:

<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.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
</dict>

创建bash脚本

resource/notarize-for-mac
(赋予其可执行权限),内容为:

#!/bin/sh
# notarize-for-mac
# Notarize Java executable for Mac distribution.
#
# Batch file must be run with the project directory as the working directory.

# variables
USER_ID="[email protected]"
ENTITLEMENTS_FILE="resource/mac-entitlements.plist"
CERT_ID="Developer ID Application: XYZ Signing ID (ABCDEFGHIJK)"

# derived variables
APP_NAME="MyApp Name"
APP_PATH="deploy-mac/$APP_NAME.app"
ZIP_PATH="deploy-mac/$APP_NAME.zip"
BIN_PATH_MACOS="$APP_PATH/Contents/MacOS"
BIN_PATH_HOMELIB="$APP_PATH/Contents/runtime/Contents/Home/lib"
BIN_PATH_RUN_MACOS="$APP_PATH/Contents/runtime/Contents/MacOS"

# do NOT change the order of these; notarization will fail
BINARIES=(\
    "$BIN_PATH_HOMELIB/jspawnhelper" \
    "$BIN_PATH_RUN_MACOS/libjli.dylib" \
    "$BIN_PATH_MACOS/$APP_NAME" \
)

DYLIBS=(\
    "$BIN_PATH_HOMELIB/"*".dylib" \
    "$BIN_PATH_HOMELIB/server/"*".dylib" \
    "$BIN_PATH_RUN_MACOS/"*".dylib" \
)

if [[ "$1" = "sign" ]]
then
    # sign the DLLs first
    codesign \
        --sign "$CERT_ID" \
        --force \
        --options runtime \
        -vvv "${DYLIBS[@]}"

    # sign the binaries; these MUST be signed with --strict so that the
    # the Gatekeeper doesn't choke on them; the --entitlements are needed
    # because the Gatekeeper doesn't normally allow JIT compiling (thanks
    # for nothing, Apple)
    codesign \
        --sign "$CERT_ID" \
        --force \
        --strict \
        --entitlements "$ENTITLEMENTS_FILE" \
        --options runtime \
        -vvv "${BINARIES[@]}"

    # sign the bundle
    codesign \
        --sign "$CERT_ID" \
        --verify \
        --force \
        --options runtime \
        -vvv "$APP_PATH"

elif [[ "$1" = "setpwd" ]]
then
    # load user ID/password information onto keychain
    echo "Enter notary tool password: \c"
    read NOTARY_PASSWORD

    xcrun notarytool store-credentials "notarytool-password" \
        --apple-id "$USER_ID" \
        --password "$NOTARY_PASSWORD"

elif [[ "$1" = "notarize" ]]
then
    # package as .zip file
    rm "$ZIP_PATH"
    ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"

    # submit app for notarization
    xcrun notarytool submit "$ZIP_PATH" \
        --keychain-profile "notarytool-password" \
        --wait

    # if the command returns an error code, use
    #    xcrun notarytool log <ticket_ID> --keychain-profile "notarytool-password" \ 
    #       notary_error_log.json
    # to obtain a list of errors to remedy

elif [[ "$1" = "staple" ]]
then
    # staple Gatekeeper ticket to signed application
    xcrun stapler staple "$APP_PATH"

elif [[ "$1" = "zip" ]]
then
    # zip stapled app for distribution
    rm "$ZIP_PATH"
    ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
fi

仅限一次,请致电

./resource/notarize-for-mac setpwd

并输入您的应用程序专用密码,将其加载到本地钥匙串上的“notarytool-password”ID 中。您也可以将密码直接插入脚本中,但如果您需要共享/分发脚本,这是一个坏主意,并且是不好的安全实践。

完成此操作后,每次您想要签署应用程序时,请使用(按顺序):

./resource/notarize-for-mac sign
- 签署应用程序包
./resource/notarize-for-mac notarize
- 将捆绑包提交给 Apple 的公证服务
./resource/notarize-for-mac staple
- 将公证票钉到捆绑包中(假设公证成功)
./resource/notarize-for-mac zip
- 获取压缩版本的捆绑包进行分发

从 JDK 21 开始,这个过程对我有效。

© www.soinside.com 2019 - 2024. All rights reserved.