16

我用 Qt5 开发了一个 Mac 应用程序,所以在 Xcode 之外。我希望 GateKeeper 允许我的应用程序在客户的计算机上运行,​​而不是发出“无法打开,因为无法确认开发人员的身份”警告。

我已经成功地对应用程序进行了数字签名,但 GateKeeper 仍然带有此投诉。我有一个 Apple 开发者证书(我是团队代理),我的钥匙串说它是有效的。我还安装了两个 Apple 根证书。

我使用命令行实用程序codesign对 app 文件夹中的所有二进制文件进行数字签名,此外我还对 app 文件夹本身进行了数字签名。在所有情况下,codesign 的响应都是信息丰富的,并且没有显示错误。使用 codesign,我可以检查所有二进制文件是否都已签名,正在运行

$ codesign --verify --deep --verbose=2 MyApp.app

表明所有二进制文件都经过验证。此外,它还报告:

MyApp.app:在磁盘上有效
MyApp.app:满足其指定要求

跑步:

$ codesign -v --verbose=4 --display MyApp.app   

可执行文件=/Users/xxx/trunk/yyy/deploy/release/MyApp.app/Contents/MacOS/MyApp
Identifier=aaaa.MyApp
格式=与 Mach-O Thin (x86_64)
捆绑 CodeDirectory v=20200 size=12461 flags=0x0 (none) hashes=616+3 location=embedded
Hash type=sha1 size=20
CDHash=d1c12c783dac0e8d9a2b749fb896b11558cec8b6
Signature size=8532
Authority=Developer ID Application: XXXXX
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=29 jul. 2015 12;04:40
Info.plist 条目=8
TeamIdentifier=YYYYY
密封资源版本=2 规则=12 文件=10
内部要求计数=1 大小=180

这似乎没问题。

跑步

$ spctl -a -t exec -vv MyApp.app

所有二进制文件上给出的结果

MyApp.app:接受
来源=开发者 ID
来源=开发者 ID 应用程序:XXXX

这似乎也不错

在应用程序或应用程序文件夹内的二进制文件上运行 XCode 命令行工具检查签名:

$ ./check-signature /Users/xxx/trunk/yyy/release/MyApp.app

结果给出

(c) 2014 Apple Inc. 保留所有权利。
是的

这在所有情况下都是期望的结果。

但是GateKeeper仍然不接受该应用程序并抱怨无法确认开发者的事实。

[作者于 2015 年 7 月 17 日星期五添加]

我想我找到了问题所在。我不知道它是一个功能还是一个 OSX 错误。stackoverflow问题 19551298帮助了我很多。

每当从 Internet 下载文件时,它都会获得与其关联的扩展文件属性 com.apple.quarantine。在 Finder 中双击这个下载的文件时,GateKeeper 有两种可能:

  1. 当文件未签名时,它会发出“Unidentified developer etc”消息

  2. 当文件经过数字签名时,它会发出“无法确认开发人员等”消息

在这两种情况下,MessageBox 都只有一个按钮,即确定按钮。单击此按钮时,除了 MessageBox 关闭之外,没有任何反应。

如果扩展属性被删除(xattr -d)应用程序运行,签名与否。

当应用程序通过在应用程序上的 Finder 中单击鼠标右键然后单击“打开”菜单操作来启动应用程序时,行为会有所不同。再次显示两个消息框之一,但现在有一个额外的按钮,允许用户无论如何打开应用程序。同样,签名和未签名之间的唯一区别是“未识别”或“未确认”消息。我不希望我的客户能够分辨出不同之处。因此,签署应用程序是徒劳的。

根据Apple 支持文档,我期望在双击下载的应用程序时 GateKeeper 的另一个更好的行为(可能文档已经过时,或者我误读了它):

  1. 如果应用程序已签名,GateKeeper 应该显示一个带有“从互联网等下载”的消息框和一个带有“仍然继续?”的按钮。

  2. 如果应用程序未使用单个确定按钮和文本“未识别的开发人员等”签署 MessageBox。

4

5 回答 5

18

很抱歉回答我自己的问题,但我认为没有其他方法可以编辑原始问题会导致意大利面条文本。

我终于解决了我的问题。首先是功劳:(i)我的另一个 stackoverflow问题的答案非常有用,并且(ii)通过提交所谓的技术支持事件(TSI),我从 Apple 官方开发人员那里得到了非常好的(付费)建议。

基于所有这些,我现在可以在这里给出一个非常简洁的秘诀,说明如何让 GateKeeper 成功处理您的 Mac 应用程序。在详细说明食谱后,我将展示我最初的错误是什么。

目标:在 Xcode 之外开发了一个 Mac 应用程序后,让 GateKeeper 发出警告“从 Internet 下载......”,三个按钮,其中一个是“打开”。

失败:当 GateKeeper 发出带有文本“.. unidentified developer..”或文本“.. unconfirmed developer..”的警告时,在这两种情况下都带有一个带有单个 OK 按钮的消息框。

准备好您的应用程序 GateKeeper 涉及三个步骤:

  • 使您的应用程序独立,没有不可接受的外部依赖项。唯一可接受的外部依赖是系统库。所有其他依赖项都应该已复制到您的 MyApp.app 文件夹中。GateKeeper 拒绝任何具有非系统外部依赖项的应用程序
  • 二进制文件不应位于 MyApp.app 文件夹内的非法位置。库进入 MyApp/Contents/Frameworks,可执行文件进入 MyApp/Contents/MacOS
  • MyApp 中的所有二进制文件都应进行数字签名。然后应该对 MyApp.app 文件夹进行签名。对于此签名,需要 Apple“开发者 ID 应用程序...”证书

我们的食谱是自动的。所有工作都由一个脚本完成。对于 Qt Creator,我们使用 qmake 脚本,通过$$system命令访问系统 shell。使用任一(Xcode)系统命令codesign时,spctl或者check-signature 我们假设您已将 stderr 重定向到 stdout,如问题答案中所述。否则,您在运行这些实用程序时将无法捕捉到系统响应。在下文中,我们将不明确显示此重定向。

这是我们的食谱

A. 使应用程序独立:

  1. 将所有需要的二进制文件复制(使用脚本)到 MyApp.folder
  2. 运行(使用脚本) install_name_tool -changeinstall_name_tool -id 使得应用程序内的所有依赖项都是相对类型@executable_path/../MacOS..@executable_path/../Frameworks
  3. 在 MyApp.app 文件夹内的所有二进制文件上运行(使用脚本)otool -L并标记任何非法依赖,例如“@rpath ...”或不是系统路径的绝对文件路径。请注意,otool -L不能保证找到所有依赖项。插件通常超出otool. 这就是为什么您需要进行下一次检查的原因。
  4. 在“MyApp.app/Contents/MacOS”位置启动一个终端。运行export DYLD_PRINT_LIBRARIES=1。然后在同一个终端窗口内运行./MyApp。您的终端将装满数百个已加载的库。再次检查此列表以查找被禁止的库(库存在于您的计算机上,但不在您的客户的计算机上)。
  5. 布丁的证据在吃。我们使用MacInCloud 虚拟机并检查我们的应用程序是否在那里运行。替代解决方案可能是不是开发人员的亲戚的 Mac。或者您也可以在您自己的 Mac 上创建一个新用户(“测试”)并将应用程序复制到其下载(或桌面文件夹,或...)。在后一种情况下,您必须临时重命名 IDE 的根文件夹,否则用户“test”将在那里找到丢失的二进制文件。

B 签署应用程序

  1. 签名:使用我们的脚本,我们codesign --force --verify --verbose --sign \"Developer ID Application: ....\" \"/path/to/binary\" 在应用程序中的所有二进制文件上运行,然后在应用程序文件夹本身上运行。在每种情况下,系统响应都会被捕获。它应该在每种情况下都包含字符串“signed Mach-O thin”。
  2. 验证:在应用程序中的每个二进制文件和应用程序本身上运行(使用脚本)命令codesign --verify --verbose \"/path/to/binary\"并捕获系统响应。在每种情况下,它都应包含字符串“在磁盘上有效”和“满足其指定要求”。
  3. spctl -a -t exec -vv /path/to/binary\" GateKeeper 检查:在每个二进制文件和应用程序文件夹本身上运行(使用脚本) 。系统响应被捕获。它应该在所有情况下都包含字符串“accepted source”。
  4. check-signature \"/path/to/banary\" check-signature:在每个二进制文件和应用程序文件夹本身上运行(使用脚本) 。系统响应被捕获。在每种情况下,它都应包含字符串“YES”。

C 外部检查

  1. 将您的应用压缩到一个 zip 文件中。上传到您的云服务器之一

  2. GateKeepers 在其一般的看门人角色上保留了一个长长的异常列表(通常为数百项)。如果您想测试 GateKeeper,您的应用程序不得在该列表中。与编辑此列表相比,一个更简单的技巧是在您的 Mac 上创建一个新用户。登录该用户并从 Internet 云服务器下载 zip 文件。Finder 会自动解压。点击它。如果 GateKeeper 告诉您它可以打开应用程序,但它在从 Internet 下载它的同时警告您,那么是时候喝一杯(白)啤酒了。

这里是所需的 GateKeeper 警告:在此处输入图像描述

我的错

我在没有明确检查每个二进制文件的结果的情况下进行了大部分安装和签名。之后我会otool -L在一些二进制文件上使用,但不是全部。我错过了从早期 Qt 版本升级到 Qt 5.5 二进制文件libqminimal.dylib已经获得额外依赖项的事实,即:QtDBus. 我没有注意到它,但 GateKeeper 注意到了。

Qt 开发人员可能想知道为什么我们不只是macdeployqt用于在 Mac 上部署 Qt 应用程序。首先,我们不喜欢不使用记录不充分的黑盒实用程序。在 Internet 论坛上,有很多人报告macdeployqt. 此外,otool-L在比较不同的 Qt 版本时,Qt 库可以有不同的安装位置(由 报告)。当我们有一个新的 Qt 版本时,我们的脚本将立即开始大喊禁止依赖项。通过这种方式,我们可以获得有关此新版本中更改内容的信息。

于 2015-08-15T14:01:51.657 回答
1

adlag 的问题和自我回答对于帮助我克服同样的问题非常宝贵。然而,尽管他的食谱很好,但有些说法并不完全正确,所以我想补充几点。

  • 不必用 @executable_path 语句替换二进制文件和动态库中的 @rpath 条目。只要嵌入在二进制文件中的实际 rpath 条目不是绝对的,@rpath 语句就可以了。您可以找到大量使用 rpath 的有效 Qt 应用程序包。你可以按照 adlag 的说法让它工作,但你可能正在为自己工作。
  • 有关如何使用otool -l $file | grep -A2 LC_RPATH以及install_name_tool -delete_rpath $path $file检查和删除二进制文件和库中的嵌入路径,请参阅上面的 jil 评论
  • 请参阅https://developer.apple.com/library/content/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG207清楚地解释为什么 GateKeeper 抱怨您的二进制文件中的路径,以及如何在 syslog 中查看具体投诉。
  • 如果您对绝对路径有疑问,您应该首先尝试修复您的构建,而不是install_name_tool事后使用。
  • 如果您使用的是 cmake,这可能会有所帮助:https ://cmake.org/Wiki/CMake_RPATH_handling#Mac_OS_X_and_the_RPATH
  • 不要spctl -a -t exec -vv /path/to/binary在 dylib 文件上运行。您将收到有关资源信封的错误。这是意料之中的,不是问题。
  • 以我的经验,macdeployqt工作正常。我通过更改构建解决了这个问题,这样绝对路径就不会进入有问题的 dylib 文件(libquazip)。我仍然习惯于install_name_tool删除 Qt 安装的绝对路径。然后我用来macdeployqt创建捆绑包、签署捆绑包并创建 DMG 文件。
于 2017-05-09T12:32:53.033 回答
1

我的两点:

  1. 要真正验证协同设计,我必须将我的 DMG 上传到服务器并使用浏览器下载它,或者手动设置隔离属性

    APP_PATH="Any.app"
    xattr -w com.apple.quarantine '0081;5a37dc6a;Google Chrome;F15F7E1C-F894-4B7D-91B4-E110D11C4858' "$APP_PATH"
    xattr -l "$APP_PATH" # You should see the quarantine attribute here
    open "$APP_PATH"
    

    如果您的应用程序已正确签名,您应该会看到一个带有“打开”按钮的系统对话框。

    我通过查看从 Internet 下载的另一个 .app 找到了隔离属性的值。我不知道值是什么意思。

    我真的不明白为什么 spctl 命令说“接受”即使 Gatekeeper 服务拒绝打开应用程序。

  2. 我有“身份不明的开发人员”消息框,因为我的 Qt 框架被引用为“ @rpath /QtCore.framework”。使用 install_name_tool将其更改为“ @application_path/../Frameworks /QtCore.framework”解决了我的应用程序中的问题。

于 2017-12-19T14:49:28.803 回答
0

多次尝试后发现问题所在。

就我而言:弹出消息 - 由于缺少库而损坏的应用程序。我使用 QT 创建了 .app 文件。为了生成 dmg,我使用了 deploymacqt 命令工具。deploymacqt 工具在 .app 中创建动态库,所以基本上如果我们在创建 dmg 之前进行代码签名,这个修改将操纵代码签名。所以正确的解决方法是。

# Create dmg using 
    deploymacqt <yourapp.app> -dmg

# Open resulted dmg file, copy <yourapp.app> to different folder(let's say /Documents/<yourapp.app>)

# Codesign the /Documents/<yourapp.app> using 
    codesign --deep --force --verify --verbose --sign "Developer ID Application: <developerid>" <yourapp.app>

# Verify using
    codesign --verify --verbose=4 <yourapp.app>
 * you should see something like this
    <yourapp.app>: valid on disk
    <yourapp.app>: satisfies its Designated Requirement

# Now create again the dmg file using dropdmg(https://c-command.com/dropdmg/) application, download, install dropdmg. set the cofiguration preferences with your developer id certificate in signing option.

# drag and drop <yourapp.app> to dropdmg app, wait for creation of dmg to complete. voila you have now successfully created dmg with proper developer id certification.

# verify resulted dmg again using   
     codesign --verify --verbose=4 <yourapp.dmg>
# you can also verify with gatekeeper
     spctl -a -t exec -vv <yourapp.dmg>

完成这些操作后,您将不会看到弹出消息说应用程序已损坏或损坏或身份不明的开发人员。

于 2017-06-30T13:12:12.380 回答
0

otool -l您需要使用可执行文件的命令行和您的框架来验证所有 rpath 。如果您的可执行文件中有本地 rpath(例如:/user/name/Qt/),请删除它(使用此命令install_name_tool -delete_rpath)。

于 2019-04-23T13:47:17.613 回答