首页 文章

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

提问于
浏览
15

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

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

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

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

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

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

运行:

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

可执行= /用户/ XXX /中继/ YYY /部署/释放/ MyApp.app /内容/ MacOS的/ MyApp的标识符= aaaa.MyApp格式=马赫-O薄(x86_64的)CodeDirectory v捆绑= 20200大小= 12461个标志=为0x0(无)散列= 616 3位置=嵌入式哈希类型= SHA1大小= 20 CDHash = d1c12c783dac0e8d9a2b749fb896b11558cec8b6签名大小= 8532管理局=开发者ID的应用:XXXXX管理局=开发者ID的认证中心局=苹果根CA时间戳= 7月29日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

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

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

这也似乎没问题

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

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

给出结果

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

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

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

[added by author on Friday July 17, 2015]

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

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

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

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

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

如果删除了扩展属性(xattr -d),应用程序将运行,是否已签名 .

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

Apple Support Documentation我预期的另一个基础,把关更漂亮行为时,双击下载的应用程序(可能是文件已经过时,还是我看错了):

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

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

4 回答

  • 16

    adlag的问题和自我回答对帮助我克服同样的问题非常宝贵 . 然而,和他的食谱一样好,有些陈述不太正确,所以我想提供一些额外的观点 .

    • 没有必要用@executable_path语句替换二进制文件和动态库中的@rpath条目 . 只要嵌入在二进制文件中的实际rpath条目不是绝对的,@rpath语句就可以了 . 您可以找到许多使用rpath的有效Qt应用程序包 . 你可以按照adlag的说法开展工作,但你可能正在为自己工作 .

    • 请参阅上面的jil评论,了解如何使用 otool -l $file | grep -A2 LC_RPATHinstall_name_tool -delete_rpath $path $file 来检查和删除二进制文件和库中的嵌入路径

    • 请参阅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文件 .

  • 1

    经过多次尝试后想出了问题 .

    在我的情况下:流行消息 - 损坏的应用程序是由于库缺失 . 我使用QT创建了.app文件 . 要生成dmg,我使用的是deploymacqt命令工具 . deploymacqt工具在.app中创建动态库,所以基本上如果我们在创建dmg之前进行协同编码,这个alter会操纵代码符号 . 所以正确的解决方法是 .

    # 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>
    

    一旦完成这些操作,您将看不到弹出消息,说应用已损坏或损坏或未识别的开发人员 .

  • 1

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

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

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

    目标:在Xcode之外开发了一个Mac应用程序,让GateKeeper发出警告“从Internet上下载...”,其中有三个按钮,其中一个是“打开” . 失败:当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 . 使用answer to question中概述的(Xcode)系统命令 codesignspctlcheck-signature we assume you have redirected stderr to stdout 时 . 否则,在运行这些实用程序时,您将无法捕获系统响应 . 在下文中,我们不会明确显示此重定向 .

    HERE IS OUR RECIPE

    A.使应用程序独立:将所有需要的二进制文件复制到MyApp.folder运行(使用脚本)install_name_tool -change和install_name_tool -id,以便应用程序内的所有依赖项都是相对类型@executable_path /../ MacOS ..或@executable_path /../框架在MyApp.app文件夹内的所有二进制文件上运行(使用脚本)otool -L并标记任何非法依赖,例如“@rpath ...”或绝对文件路径不是系统路径 . 请注意,otool -L不能保证找到所有依赖项 . 插件通常超出了otool的范围 . 这就是你需要下一次检查的原因 . 在“MyApp.app/Contents/MacOS”位置启动终端 . 运行导出DYLD_PRINT_LIBRARIES = 1 . 然后在同一个终端窗口./MyApp内运行 . 您的终端将填满数百个已加载的库 . 再次检查此列表是否有禁止的库(计算机上存在的库,但客户的计算机上没有) . 布丁的证据就在吃 . 我们使用MacInCloud虚拟机并检查我们的应用程序是否在那里运行 . 替代解决方案可能是不是开发人员的亲属的Mac . 或者您也可以在自己的Mac上创建新用户(“测试”)并将应用程序复制到其下载(或桌面文件夹,或...) . 在后一种情况下,您必须临时重命名IDE的根文件夹,否则用户“test”将在那里找到丢失的二进制文件 . B签署应用程序签名:使用我们的脚本,我们在所有二进制文件上运行codesign --force --verify --verbose --sign \“Developer ID Application:.... \”\“/ path / to / binary \”在应用程序中,然后在应用程序文件夹本身 . 在每种情况下都会捕获系统响应 . 它应该在每种情况下都包含字符串“signed Mach-O thin” . 验证:在应用程序中的每个二进制文件和应用程序本身上运行(使用脚本)命令codesign --verify --verbose \“/ path / to / binary \”并捕获系统响应 . 它应该在每种情况下包含“有效的字符串”磁盘“和”满足其指定要求“.GateKeeper检查:在每个二进制文件和app文件夹本身上运行(使用脚本)spctl -a -t exec -vv / path / to / binary \” . 系统响应被捕获 . 它应该在所有情况下都包含字符串“accepted source” . check-signature:在每个二进制文件和app文件夹本身上运行(带脚本)check-signature \“/ path / to / banary \” . 系统响应被捕获 . 它应该包含每种情况下的字符串“YES” . C外部检查将您的应用压缩到单个zip文件中 . 上传到您的一个 Cloud 服务器GateKeepers在其一般的守门员角色上保留了长列表(通常是数百个项目) . 如果您想测试GateKeeper,您的应用必须不在该列表中 . 而不是编辑此列表更简单的技巧是在Mac上创建新用户 . 登录该用户并从Internet Cloud 服务器下载zip文件 . Finder会自动解压缩它 . 点击它 . 如果GateKeeper告诉您它可以打开应用程序,但它会在从互联网上下载的同时向您发出警告,那么就该抓一个(白色)啤酒了 .

    这里是所需的GateKeeper警告:
    enter image description here

    My mistake

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

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

  • 0

    我的两位:

    • 要真正验证代码签名,我必须将我的DMG上传到服务器并使用浏览器或 set the quarantine attribute manually 下载:
    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找到了隔离属性的值 . 我不知道这个 Value 意味着什么 .

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

    • 我有"unidentified developer"消息框,因为我的Qt框架被引用为“ @rpath /QtCore.framework ". Changing it to " @application_path/../Frameworks /QtCore.framework”,使用install_name_tool修复了我的应用中的问题 .

相关问题