无法在Xcode外部以数字方式签署Mac应用程序

adl*_*lag 15 macos xcode cocoa code-signing osx-gatekeeper

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

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

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

$ codesign --verify --deep --verbose=2 MyApp.app
Run Code Online (Sandbox Code Playgroud)

显示所有二进制文件都已验证.此外,它报告:

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

运行:

$ codesign -v --verbose=4 --display MyApp.app   
Run Code Online (Sandbox Code Playgroud)

Executable =/Users/xxx/trunk/yyy/deploy/release/MyApp.app/Contents/MacOS/MyApp
Identifier = aaaa.MyApp
Format = bundle with Mach-O thin(x86_64)
CodeDirectory v = 20200 size = 12461 flags = 0x0 (无)散列= 616 + 3位置=嵌入
散列类型= sha1大小= 20
CDHash = d1c12c783dac0e8d9a2b749fb896b11558cec8b6
签名大小= 8532
授权=开发者ID应用程序:XXXXX
授权=开发者ID证书颁发机构
授权= Apple根CA
时间戳= 29 jul.2015 12; 04:40
Info.plist entries = 8
TeamIdentifier = YYYYY
Sealed Resources version = 2 rules = 12 files = 10
内部要求count = 1 size = 180

这似乎没问题.

运行

$ spctl -a -t exec -vv MyApp.app
Run Code Online (Sandbox Code Playgroud)

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

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

这也似乎没问题

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

$ ./check-signature /Users/xxx/trunk/yyy/release/MyApp.app
Run Code Online (Sandbox Code Playgroud)

给出结果

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

所有情况下都是理想的结果.

但GateKeeper仍然不接受该应用程序,并抱怨开发人员无法确认.

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

我想我已经找到了问题.我不知道它是一个功能还是一个OSX错误.stackoverflow 问题19551298给了我很多帮助.

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

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

  2. 当文件经过数字签名时,它会发出"Developer not be determined etc"消息

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

如果删除了扩展属性(xattr -d),则应用程序运行,签名或不签名.

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

Apple支持文档的基础上,我预计当双击下载的应用程序时GateKeeper的另一个更好的行为(可能是文档已过时,或者我误读了它):

  1. 如果应用程序已签名,GateKeeper应显示一个MessageBox,其中包含"从Internet上下载等"和一个"仍然继续进行"的按钮?

  2. 如果应用程序没有使用单个"确定"按钮和"未识别的开发人员等"文本对MessageBox进行签名.

adl*_*lag 17

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

我终于解决了我的问题.首先是信用:(i)我的其他stackoverflow 问题的答案是非常有用的,并且(ii)我通过提交所谓的技术支持事件(TSI)从Apple官方开发者那里获得了很好的(付费)建议.

基于这一切,我现在能够在这里给出一个非常简洁的方法,说明如何通过GateKeeper成功处理您的Mac应用程序.详细说明食谱后,我将展示我原来的错误.

目标:在Xcode之外开发了一个Mac应用程序,让GateKeeper发出警告"从互联网下载...",有三个按钮,其中一个是"打开".

失败:当GateKeeper发出警告时,文本".. unidentified developer .."或文本"..未经证实的开发人员......" - 在两种情况下 - 带有一个OK按钮的消息框.

获取应用程序GateKeeper就绪包括三个步骤:

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

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

这是我们的食谱

A.使应用程序独立:

  1. 将所有需要的二进制文件(带脚本)复制到MyApp.folder
  2. 运行(使用脚本) install_name_tool -change并且install_name_tool -id 应用程序内的所有依赖关系都是相对类型@executable_path/../MacOS..@executable_path/../Frameworks
  3. otool -L在MyApp.app文件夹中的所有二进制文件上运行(使用脚本)并标记任何非法依赖,例如"@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\"在应用程序中的所有二进制文件上运行,然后在app文件夹本身上运行.在每种情况下都会捕获系统响应.它应该在每种情况下都包含字符串"signed Mach-O thin".
  2. 验证:codesign --verify --verbose \"/path/to/binary\"对应用程序中的每个二进制文件和应用程序本身运行(使用脚本)命令,并捕获系统响应.它应该在每种情况下包含字符串"在磁盘上有效"和"满足其指定要求".
  3. GateKeeper检查:spctl -a -t exec -vv /path/to/binary\"在每个二进制文件和app文件夹本身上运行(带脚本).捕获系统响应.它应该在所有情况下都包含字符串"accepted source".
  4. check-signature:check-signature \"/path/to/banary\"在每个二进制文件和app文件夹本身上运行(带脚本).捕获系统响应.在每种情况下,它应包含字符串"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应用程序.首先,我们不喜欢不使用记录不良的黑盒实用程序.在互联网论坛上,有很多人报道了问题macdeployqt.此外,otool-L在比较不同的Qt版本时,Qt库可以具有不同的安装位置(如报告所示).当我们有一个新的Qt版本时,我们的脚本将立即开始对禁止的依赖关系大喊大叫.通过这种方式,我们可以获得有关此新版本中已更改内容的信息.

  • 还有值得检查加载命令中没有绝对路径,即应用程序包运行中的每个二进制文件:`otool -l $ file | grep -A2 LC_RPATH`.如果你发现任何,你可以使用`install_name_tool -delete_rpath $ path $ file`删除它们. (2认同)