MacOS:为什么 PortJump 包上的 'spctl --assess' return 'no usable signature' 在签名和公证后看似成功?
MacOS: Why does 'spctl --assess' return 'no usable signature' on a PortJump package, after signing and notarizing have seemingly succeeded?
[注: 如果背景信息无关紧要,请跳至下面的更新给你]
我有一个用于 Windows 的开源会计软件,并且有一个由 Codeweavers 构建的 Mac 端口(Wine 和 Crossover 在后台工作)。他们是聪明、友善和乐于助人的人,但他们的 PortJump 产品的客户文档为零(尽管 Codeweavers 声称已经制作端口一千次,但在互联网上的其他地方找不到任何东西)。也许我的问题太菜鸟了,他们甚至无法了解我的情况。所以我现在为自己尝试了几个月,绝望导致我的无力尝试之间的间隔越来越长。
我有一个 .zip 文件中的 .app 包,如果需要,您可以在这里找到:
https://www.codeweavers.com/xfer/oems/EasyCashTax/easyct-2.38.3-unsigned.zip
访问码:MUTmlUVm
在 Apple 开发人员门户上,我使用证书名称创建了分发标识
“Thomas Mielke”和应用程序 ID“de.easyct.easyct”的配置文件。 (如果需要,我还有来自官方 CA 的 X.509 软件代码签名证书。)
也许我应该做的第一件事就是签署代码,就像这个问题一样:
codesign --deep on mavericks xcode 5.0 (5A1412)
或者这根本不应该是第一步...我完全被整个 Mac 环境所疏远,并且总觉得一次有太多未解决的问题而无法开始黑客攻击(为什么这是一个 zip 而不是 dmg?我必须签署多深以及有哪些选项?为什么这不能成为一个我可以简单地在 Xcode 中打开并使用 Organizer 签署的项目?)。
也许有人可以引导我到一个安全的地方,在那里我可以开始感到舒服并进入快乐的尝试和错误循环......或者,换句话说:如果你必须维护一个 PortJump 包,什么会是你的方法:git 回购,自制软件,Xcode,shell 脚本或使用 other software?
这里有 Mac 开发人员可以向我展示在 MacOS 上开发的魅力和力量吗?
更新:
我现在得到这个脚本来签署我的包裹:
#!/bin/bash
MAC_SIGNING_IDENTITY="Developer ID Application:"
entitlements="wine32on64.entitlements"
app=""
product_id=
bundle_id=
SRCROOT=.
if [ ! -f $entitlements ]
then
echo "$entitlements not found. Make sure it's in your working directory."
exit 1
fi
if [ -z "$app" ]
then
echo "You must specify the absolute path to the .app"
exit 1
fi
if [ ! -d "$app" ]
then
echo "The path You specify is invalid. Please provide the absolute path to the .app"
exit 1
fi
if [[ ! "$app" = /* ]]
then
echo "The path you specified is not an absolute path. Please provide the absolue path to the .app"
exit 1
fi
if [ -z "$bundle_id" ]
then
bundle_id=`defaults read "$app/Contents/Info.plist" CFBundleIdentifier`
if [ -z "$bundle_id" ]
then
echo "Could not determine the product name from '$app'. Did you provide the absolute path to the .app?"
exit 1
fi
fi
echo "Bundle ID = \"$bundle_id\""
if [ -z "$product_id" ]
then
product_id=`ls -d "$app/Contents/SharedSupport"/* | grep -v '/X11'`
if [ ! -d "$product_id" ]
then
echo "could not determine the product id from '$app'"
exit 1
fi
product_id=`basename "$product_id"`
echo "$product_id" | LC_ALL=C egrep '^[a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_]*$' >/dev/null
if [ $? -ne 0 ]
then
echo "the product id '$product_id' is not valid"
exit 1
fi
fi
echo "Product ID = \"$product_id\""
keychain=$(security find-certificate -c "$MAC_SIGNING_IDENTITY" | grep keychain | awk 'gsub(/"/, "", ) {print }')
locked=$(security show-keychain-info "$keychain" 2>&1 | grep "timeout")
if [ -z "$locked" ]
then
echo "Failed to find unlocked keychain with required certificate. Is your certificate in an unlocked keychain in your keychain search path?"
echo "Your keychain search path is:"
security list-keychain
exit 1
fi
if [ "$MAC_SIGNING_IDENTITY" != "-" ] ; then
# Figure out the Organizational Unit (OU) from the signing identity
ou=$(
set -x
security find-certificate -p -c "$MAC_SIGNING_IDENTITY" | \
openssl x509 -inform PEM -subject -noout -nameopt sname,sep_multiline,space_eq | \
awk '/ OU = / {print }'
)
if [ -z "$ou" ]; then
echo "error: Could not determine OU from signing identity '$MAC_SIGNING_IDENTITY'"
exit 1
fi
fi
set -e
# Sign the app. The designated requirements were obtained by watching what Xcode 4.3
# does when it signs for Developer ID.
function sign_one()
{
file=""; shift
identifier=""; shift
if [ "$MAC_SIGNING_IDENTITY" = "-" ] ; then
codesign --sign "$MAC_SIGNING_IDENTITY" \
--force \
"$file" "$@"
else
codesign --sign "$MAC_SIGNING_IDENTITY" \
--force \
--requirements "=designated => anchor apple generic and identifier \"$identifier\" \
and ((cert leaf[field.1.2.840.113635.100.6.1.9] exists) or \
(certificate 1[field.1.2.840.113635.100.6.2.6] exists and \
certificate leaf[field.1.2.840.113635.100.6.1.13] exists and certificate leaf[subject.OU] = \"$ou\" \
))" \
"$file" "$@"
fi
}
function sign_subdir()
{
subdir="" ; shift
id_component="" ; shift
find "$subdir/" -type f \( -name "*.so" -o -name "*dylib" -o -exec sh -c 'file "[=13=]" | fgrep -qsw Mach-O' {} \; \) -print0 |
while IFS= read -r -d '' file ; do
name=$(basename "$file")
name="${name//[^-a-zA-Z0-9]/-}"
if [ -z "${name/#[^a-zA-Z]*}" ] ; then
name="a-$name"
fi
if [ -z "${name/%*[^a-zA-Z0-9]}" ] ; then
name="$name-0"
fi
identifier="$bundle_id.$id_component.$name"
sign_one "$file" "$identifier" --identifier "$identifier" "$@"
done
}
set -x
# Sign Sparkle framework and pyobjc bundle separately from the app bundle
if [ -d "$app/Contents/Frameworks/Sparkle.framework" ]; then
sign_one "$app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/finish_installation.app" "org.andymatuschak.sparkle.finish-installation" --options runtime
sign_one "$app/Contents/Frameworks/Sparkle.framework" "org.andymatuschak.Sparkle"
fi
sign_subdir "$app/Contents/SharedSupport/$product_id/bin" "bin" --options runtime
for libdir in "$app/Contents/SharedSupport/$product_id"/lib* ; do
sign_subdir "$libdir" "$(basename "$libdir")"
done
# The wine (pre)loaders were already signed with the bin directory, above, but
# we need to re-do it with entitlements
for i in "$app/Contents/SharedSupport/$product_id/bin"/wine*loader*; do
sign_one "$i" "$bundle_id.wineloader" \
--options runtime \
--entitlements "$SRCROOT/wine32on64.entitlements"
done
sign_one "$app" "$bundle_id" --options runtime --entitlements "$SRCROOT/wine32on64.entitlements"
权利文件wine32on64.entitlements:
<?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.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</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.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.ldt-in-64bit-process</key>
<true/>
</dict>
</plist>
而这个做公证的是:
#!/bin/bash
ditto -c -k --keepParent EasyCT.app EasyCT.zip
output=$(xcrun altool --notarize-app --primary-bundle-id de.easyct.easyct --asc-provider "MYTEAMID" -u "my@apple.id" -p "abcd-efgh-ijkl-mnop" --file EasyCT.zip)
ticket_id=$(echo "$output" | grep RequestUUID | awk '{print }')
if [ -z "$ticket_id" ]
then
echo "Error: No ticket id was returned.\n\n$output"
exit 1
fi
echo "Notarization ticket: $ticket_id"
xcrun altool --notarization-info "$ticket_id" -u "my@apple.id" -p "abcd-efgh-ijkl-mnop"
xcrun stapler staple EasyCT.app
spctl --assess --type open --context context:primary-signature --verbose EasyCT.zip
一切运行顺利,只有最后一行检查 resulting packet 使用 spctl --assess
returns 我“没有可用的签名”。还有,下载包后,Gatekeeper还是需要安全例外。
问题是多方面的:首先,包中包含两个符号链接。 codesign
不介意,但 spctl -a
不喜欢。我随后用 spctl
测试了 .zip 包,即使删除了符号链接也没有通过包。在某些时候,我尝试 spctl -a
未压缩的 .app 文件夹,它起作用了。所以 spctl
似乎不喜欢 .zip 包。
这是 script 我现在用来签署和公证包裹的整理版本:
#!/bin/bash
# read config
echo "Reading myappleid.config"
. ./myappleid.config
if [ -z "$appleid" ]
then
echo "Error: Please add an entry 'appleid=<your_appleid_here>' to myappleid.config"
exit 1
fi
if [ -z "$aspw" ]
then
echo "Error: Please add an entry 'aspw=<your_app_specific_password_here>' to myappleid.config"
exit 1
fi
echo "Notarisation will use apple id '$appleid'"
# unzip archive to EasyCash&Tax.app folder
echo "Unzipping original package..."
rm -rf EasyCash\&Tax.app
unzip -q easyct-2.38.3-unsigned.zip
# delete symlinks that would prevent Gatekeeper/spctl from passing otherwise
echo "Deleting symlinks"
rm EasyCash\&Tax.app/Contents/SharedSupport/easyct/support/easyct/drive_c/users/crossover/Downloads
rm EasyCash\&Tax.app/Contents/SharedSupport/easyct/support/easyct/drive_c/users/crossover/Templates
# sign the package using Codeweavers script
echo "Signing package..."
./sign_codeV4 $(pwd)/EasyCash\&Tax.app
# archive signed package to a zip for notarisation
ditto -c -k --keepParent EasyCash\&Tax.app EasyCT.zip
# prepare option if team id was set
if [ -z "$ascprov" ]
then
ascprovoption=
else
ascprovoption="--asc-provider"
fi
output=$(xcrun altool --notarize-app --primary-bundle-id de.easyct.easyct $ascprovoption $ascprov -u "$appleid" -p "$aspw" --file EasyCT.zip)
ticket_id=$(echo "$output" | grep RequestUUID | awk '{print }')
if [ -z "$ticket_id" ]
then
echo "Error: No ticket id was returned.\n\n$output"
exit 1
fi
echo "Notarization ticket: $ticket_id"
xcrun altool --notarization-info "$ticket_id" -u "$appleid" -p "$aspw"
echo "Stapeling..."
output=$(xcrun stapler staple EasyCash\&Tax.app)
echo $output
stapling_worked=(echo "$output" | grep "The staple and validate action worked")
if [ -z "$stapling_worked" ]
then
echo "Error: stapling didn't work, it seems. Try to run 'xcrun stapler staple EasyCash\\&Tax.app' and zip EasyCT4Mac.zip manually."
exit 1
fi
echo "Checking stapled EasyCash&Tax.app folder using spctl -a..."
spctl --assess --type open --context context:primary-signature --verbose EasyCash\&Tax.app
echo "Final packaging to EasyCT4Mac.zip..."
ditto -c -k --keepParent EasyCash\&Tax.app EasyCT4Mac.zip
# clean-up temporary zip, used for notarisation
rm EasyCT.zip
希望这对遇到类似问题的人有所帮助。
[注: 如果背景信息无关紧要,请跳至下面的更新给你]
我有一个用于 Windows 的开源会计软件,并且有一个由 Codeweavers 构建的 Mac 端口(Wine 和 Crossover 在后台工作)。他们是聪明、友善和乐于助人的人,但他们的 PortJump 产品的客户文档为零(尽管 Codeweavers 声称已经制作端口一千次,但在互联网上的其他地方找不到任何东西)。也许我的问题太菜鸟了,他们甚至无法了解我的情况。所以我现在为自己尝试了几个月,绝望导致我的无力尝试之间的间隔越来越长。
我有一个 .zip 文件中的 .app 包,如果需要,您可以在这里找到:
https://www.codeweavers.com/xfer/oems/EasyCashTax/easyct-2.38.3-unsigned.zip
访问码:MUTmlUVm
在 Apple 开发人员门户上,我使用证书名称创建了分发标识 “Thomas Mielke”和应用程序 ID“de.easyct.easyct”的配置文件。 (如果需要,我还有来自官方 CA 的 X.509 软件代码签名证书。)
也许我应该做的第一件事就是签署代码,就像这个问题一样: codesign --deep on mavericks xcode 5.0 (5A1412)
或者这根本不应该是第一步...我完全被整个 Mac 环境所疏远,并且总觉得一次有太多未解决的问题而无法开始黑客攻击(为什么这是一个 zip 而不是 dmg?我必须签署多深以及有哪些选项?为什么这不能成为一个我可以简单地在 Xcode 中打开并使用 Organizer 签署的项目?)。
也许有人可以引导我到一个安全的地方,在那里我可以开始感到舒服并进入快乐的尝试和错误循环......或者,换句话说:如果你必须维护一个 PortJump 包,什么会是你的方法:git 回购,自制软件,Xcode,shell 脚本或使用 other software?
这里有 Mac 开发人员可以向我展示在 MacOS 上开发的魅力和力量吗?
更新:
我现在得到这个脚本来签署我的包裹:
#!/bin/bash
MAC_SIGNING_IDENTITY="Developer ID Application:"
entitlements="wine32on64.entitlements"
app=""
product_id=
bundle_id=
SRCROOT=.
if [ ! -f $entitlements ]
then
echo "$entitlements not found. Make sure it's in your working directory."
exit 1
fi
if [ -z "$app" ]
then
echo "You must specify the absolute path to the .app"
exit 1
fi
if [ ! -d "$app" ]
then
echo "The path You specify is invalid. Please provide the absolute path to the .app"
exit 1
fi
if [[ ! "$app" = /* ]]
then
echo "The path you specified is not an absolute path. Please provide the absolue path to the .app"
exit 1
fi
if [ -z "$bundle_id" ]
then
bundle_id=`defaults read "$app/Contents/Info.plist" CFBundleIdentifier`
if [ -z "$bundle_id" ]
then
echo "Could not determine the product name from '$app'. Did you provide the absolute path to the .app?"
exit 1
fi
fi
echo "Bundle ID = \"$bundle_id\""
if [ -z "$product_id" ]
then
product_id=`ls -d "$app/Contents/SharedSupport"/* | grep -v '/X11'`
if [ ! -d "$product_id" ]
then
echo "could not determine the product id from '$app'"
exit 1
fi
product_id=`basename "$product_id"`
echo "$product_id" | LC_ALL=C egrep '^[a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_][a-zA-Z0-9_]*$' >/dev/null
if [ $? -ne 0 ]
then
echo "the product id '$product_id' is not valid"
exit 1
fi
fi
echo "Product ID = \"$product_id\""
keychain=$(security find-certificate -c "$MAC_SIGNING_IDENTITY" | grep keychain | awk 'gsub(/"/, "", ) {print }')
locked=$(security show-keychain-info "$keychain" 2>&1 | grep "timeout")
if [ -z "$locked" ]
then
echo "Failed to find unlocked keychain with required certificate. Is your certificate in an unlocked keychain in your keychain search path?"
echo "Your keychain search path is:"
security list-keychain
exit 1
fi
if [ "$MAC_SIGNING_IDENTITY" != "-" ] ; then
# Figure out the Organizational Unit (OU) from the signing identity
ou=$(
set -x
security find-certificate -p -c "$MAC_SIGNING_IDENTITY" | \
openssl x509 -inform PEM -subject -noout -nameopt sname,sep_multiline,space_eq | \
awk '/ OU = / {print }'
)
if [ -z "$ou" ]; then
echo "error: Could not determine OU from signing identity '$MAC_SIGNING_IDENTITY'"
exit 1
fi
fi
set -e
# Sign the app. The designated requirements were obtained by watching what Xcode 4.3
# does when it signs for Developer ID.
function sign_one()
{
file=""; shift
identifier=""; shift
if [ "$MAC_SIGNING_IDENTITY" = "-" ] ; then
codesign --sign "$MAC_SIGNING_IDENTITY" \
--force \
"$file" "$@"
else
codesign --sign "$MAC_SIGNING_IDENTITY" \
--force \
--requirements "=designated => anchor apple generic and identifier \"$identifier\" \
and ((cert leaf[field.1.2.840.113635.100.6.1.9] exists) or \
(certificate 1[field.1.2.840.113635.100.6.2.6] exists and \
certificate leaf[field.1.2.840.113635.100.6.1.13] exists and certificate leaf[subject.OU] = \"$ou\" \
))" \
"$file" "$@"
fi
}
function sign_subdir()
{
subdir="" ; shift
id_component="" ; shift
find "$subdir/" -type f \( -name "*.so" -o -name "*dylib" -o -exec sh -c 'file "[=13=]" | fgrep -qsw Mach-O' {} \; \) -print0 |
while IFS= read -r -d '' file ; do
name=$(basename "$file")
name="${name//[^-a-zA-Z0-9]/-}"
if [ -z "${name/#[^a-zA-Z]*}" ] ; then
name="a-$name"
fi
if [ -z "${name/%*[^a-zA-Z0-9]}" ] ; then
name="$name-0"
fi
identifier="$bundle_id.$id_component.$name"
sign_one "$file" "$identifier" --identifier "$identifier" "$@"
done
}
set -x
# Sign Sparkle framework and pyobjc bundle separately from the app bundle
if [ -d "$app/Contents/Frameworks/Sparkle.framework" ]; then
sign_one "$app/Contents/Frameworks/Sparkle.framework/Versions/A/Resources/finish_installation.app" "org.andymatuschak.sparkle.finish-installation" --options runtime
sign_one "$app/Contents/Frameworks/Sparkle.framework" "org.andymatuschak.Sparkle"
fi
sign_subdir "$app/Contents/SharedSupport/$product_id/bin" "bin" --options runtime
for libdir in "$app/Contents/SharedSupport/$product_id"/lib* ; do
sign_subdir "$libdir" "$(basename "$libdir")"
done
# The wine (pre)loaders were already signed with the bin directory, above, but
# we need to re-do it with entitlements
for i in "$app/Contents/SharedSupport/$product_id/bin"/wine*loader*; do
sign_one "$i" "$bundle_id.wineloader" \
--options runtime \
--entitlements "$SRCROOT/wine32on64.entitlements"
done
sign_one "$app" "$bundle_id" --options runtime --entitlements "$SRCROOT/wine32on64.entitlements"
权利文件wine32on64.entitlements:
<?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.automation.apple-events</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</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.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.ldt-in-64bit-process</key>
<true/>
</dict>
</plist>
而这个做公证的是:
#!/bin/bash
ditto -c -k --keepParent EasyCT.app EasyCT.zip
output=$(xcrun altool --notarize-app --primary-bundle-id de.easyct.easyct --asc-provider "MYTEAMID" -u "my@apple.id" -p "abcd-efgh-ijkl-mnop" --file EasyCT.zip)
ticket_id=$(echo "$output" | grep RequestUUID | awk '{print }')
if [ -z "$ticket_id" ]
then
echo "Error: No ticket id was returned.\n\n$output"
exit 1
fi
echo "Notarization ticket: $ticket_id"
xcrun altool --notarization-info "$ticket_id" -u "my@apple.id" -p "abcd-efgh-ijkl-mnop"
xcrun stapler staple EasyCT.app
spctl --assess --type open --context context:primary-signature --verbose EasyCT.zip
一切运行顺利,只有最后一行检查 resulting packet 使用 spctl --assess
returns 我“没有可用的签名”。还有,下载包后,Gatekeeper还是需要安全例外。
问题是多方面的:首先,包中包含两个符号链接。 codesign
不介意,但 spctl -a
不喜欢。我随后用 spctl
测试了 .zip 包,即使删除了符号链接也没有通过包。在某些时候,我尝试 spctl -a
未压缩的 .app 文件夹,它起作用了。所以 spctl
似乎不喜欢 .zip 包。
这是 script 我现在用来签署和公证包裹的整理版本:
#!/bin/bash
# read config
echo "Reading myappleid.config"
. ./myappleid.config
if [ -z "$appleid" ]
then
echo "Error: Please add an entry 'appleid=<your_appleid_here>' to myappleid.config"
exit 1
fi
if [ -z "$aspw" ]
then
echo "Error: Please add an entry 'aspw=<your_app_specific_password_here>' to myappleid.config"
exit 1
fi
echo "Notarisation will use apple id '$appleid'"
# unzip archive to EasyCash&Tax.app folder
echo "Unzipping original package..."
rm -rf EasyCash\&Tax.app
unzip -q easyct-2.38.3-unsigned.zip
# delete symlinks that would prevent Gatekeeper/spctl from passing otherwise
echo "Deleting symlinks"
rm EasyCash\&Tax.app/Contents/SharedSupport/easyct/support/easyct/drive_c/users/crossover/Downloads
rm EasyCash\&Tax.app/Contents/SharedSupport/easyct/support/easyct/drive_c/users/crossover/Templates
# sign the package using Codeweavers script
echo "Signing package..."
./sign_codeV4 $(pwd)/EasyCash\&Tax.app
# archive signed package to a zip for notarisation
ditto -c -k --keepParent EasyCash\&Tax.app EasyCT.zip
# prepare option if team id was set
if [ -z "$ascprov" ]
then
ascprovoption=
else
ascprovoption="--asc-provider"
fi
output=$(xcrun altool --notarize-app --primary-bundle-id de.easyct.easyct $ascprovoption $ascprov -u "$appleid" -p "$aspw" --file EasyCT.zip)
ticket_id=$(echo "$output" | grep RequestUUID | awk '{print }')
if [ -z "$ticket_id" ]
then
echo "Error: No ticket id was returned.\n\n$output"
exit 1
fi
echo "Notarization ticket: $ticket_id"
xcrun altool --notarization-info "$ticket_id" -u "$appleid" -p "$aspw"
echo "Stapeling..."
output=$(xcrun stapler staple EasyCash\&Tax.app)
echo $output
stapling_worked=(echo "$output" | grep "The staple and validate action worked")
if [ -z "$stapling_worked" ]
then
echo "Error: stapling didn't work, it seems. Try to run 'xcrun stapler staple EasyCash\\&Tax.app' and zip EasyCT4Mac.zip manually."
exit 1
fi
echo "Checking stapled EasyCash&Tax.app folder using spctl -a..."
spctl --assess --type open --context context:primary-signature --verbose EasyCash\&Tax.app
echo "Final packaging to EasyCT4Mac.zip..."
ditto -c -k --keepParent EasyCash\&Tax.app EasyCT4Mac.zip
# clean-up temporary zip, used for notarisation
rm EasyCT.zip
希望这对遇到类似问题的人有所帮助。