【问题标题】:Failure digitally signing a Mac app outside Xcode在 Xcode 之外对 Mac 应用进行数字签名失败
【发布时间】:2015-07-29 19:06:21
【问题描述】:

我用 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
标识符=aaaa.MyApp
格式=捆绑 Mach-O 瘦 (x86_64)
CodeDirectory v=20200 size=12461 flags=0x0(none) hashes=616+3 location=embedded
哈希类型=sha1 大小=20
CDHash=d1c12c783dac0e8d9a2b749fb896b11558cec8b6
签名大小=8532
授权=开发者 ID 申请:XXXXX
Authority=开发者 ID 证书颁发机构
授权=Apple 根 CA
时间戳=7 月 29 日。 2015 12;04:40
Info.plist 条目=8
团队标识符 = YYYYY
密封资源版本=2 规则=12 文件=10
内部要求 count=1 size=180

这看起来不错。

跑步

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

所有 二进制文件中给出结果

MyApp.app:已接受
来源=开发者 ID
origin=开发者ID申请:XXXX

看起来也不错

运行 XCode 命令行工具 check-signature 在应用程序或应用程序文件夹内的二进制文件上:

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

给出结果

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

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

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

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

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

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

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

  2. 对文件进行数字签名后,会发出“无法确认开发人员等”消息

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

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

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

Apple Support Documentation 的基础上,我希望在双击下载的应用程序时,GateKeeper 会有另一个更好的行为(可能文档已经过时,或者我看错了):

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

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

【问题讨论】:

  • 您是在您的机器上收到 GateKeeper 错误消息,还是仅在用户机器上收到?你运行的是什么版本的 Mac OS X?什么版本的 Xcode?​​span>
  • @JWWalker 如果我使用普通帐户登录,我不会收到 GateKeeper 消息。当我以其他用户身份登录时,我确实得到了它。产生差异的原因是 Gatekeeper 为每个用户保留一份豁免清单。我还在客户的机器上收到警告消息。我正在运行 OSX Yosemite 10.10.4。 Xcode 6.4。我没有使用 Xcode 进行开发。我只是用来导出我的开发者证书。

标签: xcode macos cocoa code-signing osx-gatekeeper


【解决方案1】:

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

我终于解决了我的问题。首先是功劳:(i) 我的另一个 stackoverflow question 的答案非常有用,并且 (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) 系统命令 codesignspctlcheck-signature 我们假设您已将 stderr 重定向到 stdout,如 answer to question 中所述。否则,您在运行这些实用程序时将无法捕捉到系统响应。在下文中,我们将不明确显示此重定向。

这是我们的食谱

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 virtual machines 并检查我们的应用程序是否在那里运行。替代解决方案可能是不是开发人员的亲戚的 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. GateKeeper 检查:在每个二进制文件和应用程序文件夹本身上运行(使用脚本)spctl -a -t exec -vv /path/to/binary\" 。系统响应被捕获。它应该在所有情况下都包含字符串“accepted source”。
  4. check-signature:在每个二进制文件和应用程序文件夹本身上运行(使用脚本)check-signature \"/path/to/banary\" 。系统响应被捕获。在每种情况下,它都应包含字符串“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 的问题。此外,在比较不同的 Qt 版本时,Qt 库可以有不同的安装位置(由otool-L 报告)。当我们有一个新的 Qt 版本时,我们的脚本将立即开始大喊禁止依赖项。通过这种方式,我们可以获得有关此新版本中发生了哪些变化的信息。

【讨论】:

  • 我也面临同样的问题。在解决方案中,您在谈论哪个脚本?如果可能的话,您能否分享一个视频以更好地理解或截图。我已使用“开发者 ID 应用程序”签名归档我的应用程序。之后在执行命令时我们如何引用证书。
  • 谢谢。对我来说,问题是使用 \@loader_path 而不是 \@executable_path 作为内部框架。
  • 还值得检查加载命令中没有绝对路径,即对于应用程序包中的每个二进制文件运行:otool -l $file | grep -A2 LC_RPATH。如果您发现任何问题,您可以使用 install_name_tool -delete_rpath $path $file 删除它们。
  • 另外,我发现有用的是使用RB App Checker Lite 应用程序(我不是它的开发人员)来验证您的应用程序包。就我而言,它发现除了没有在库上设置可执行权限之外,一切都很好。结合@adlag 答案,它是救命稻草。
【解决方案2】:

我的两个位:

  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"
    

    如果您的应用已正确签名,您应该会看到一个系统对话框,其中包含 “打开”按钮。

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

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

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

【讨论】:

    【解决方案3】:

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

    • 不必用@executable_path 语句替换二进制文件和动态库中的@rpath 条目。只要嵌入在二进制文件中的实际 rpath 条目不是绝对的,@rpath 语句就可以了。您可以找到大量使用 rpath 的有效 Qt 应用程序包。你可以按照 adlag 所说的去做,但你可能正在为自己工作。
    • 有关如何使用 otool -l $file | grep -A2 LC_RPATHinstall_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
    • 不要在 dylib 文件上运行 spctl -a -t exec -vv /path/to/binary。您将收到有关资源信封的错误。这是意料之中的,不是问题。
    • 根据我的经验,macdeployqt 工作正常。我通过更改构建解决了这个问题,这样绝对路径就不会进入有问题的 dylib 文件(libquazip)。我仍然使用install_name_tool 来删除Qt 安装的绝对路径。然后我使用macdeployqt 创建捆绑包、签署捆绑包并创建 DMG 文件。

    【讨论】:

      【解决方案4】:

      经过多次尝试后发现了问题。

      就我而言: 弹出消息 - 由于缺少库而损坏的应用程序。 我使用 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>
      

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 2021-10-20
          • 1970-01-01
          • 2018-06-13
          • 2019-04-02
          • 1970-01-01
          • 2021-07-21
          • 2018-08-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多