签署OSX 10.9的框架

时间:2013-10-28 14:15:41

标签: macos qt code-signing

使用macdeployqt后,我签署了我的申请,以避免关守问题。

我可以在所有框架和捆绑包内的所有内容上使用codesign但是当我来签署捆绑包时出现错误:

$ codesign --force --verify --verbose --sign "Developer ID Application: My ID" MyApplication.app
MyApplication.app: bundle format unrecognized, invalid, or unsuitable
In subcomponent: /Users/username/Dev/Apps/MyApplication/MyApplication.app/Contents/Frameworks/QtConcurrent.framework

如果我检查签名:

$codesign -vvv MyApplication.app/Contents/Frameworks/QtConcurrent.framework/Versions/5/QtConcurrent 
MyApplication.app/Contents/Frameworks/QtConcurrent.framework/Versions/5/QtConcurrent: valid on disk
MyApplication.app/Contents/Frameworks/QtConcurrent.framework/Versions/5/QtConcurrent: satisfies its Designated Requirement

根据http://furbo.org/2013/10/17/code-signing-and-mavericks/,似乎我应该签署类似这样的框架包

$ codesign --force --verify --verbose --sign "Developer ID Application: My ID" MyApplication.app/Contents/Frameworks/QtConcurrent.framework/Versions/5

但这会导致

MyApplication.app/Contents/Frameworks/QtConcurrent.framework/Versions/5: bundle format unrecognized, invalid, or unsuitable

6 个答案:

答案 0 :(得分:19)

今天早上有同样的错误。在OSX 10.9上,codesign应用程序似乎更严格

问题是由macdeployqt没有将Qt框架捆绑包的info.plist文件复制到应用程序包中引起的。

您可以通过告诉脚本手动将文件复制到嵌入式框架的捆绑文件中来解决此问题。

e.g。

$ cp ~/Qt5.2.0/5.2.0-beta1/clang_64/lib/QtCore.framework/Contents/Info.plist MyApplication.app/Contents/Frameworks/QtCore.framework/Resources/

为您的应用使用的每个qt模块执行此操作,在调用macdeployqt *之后,但在调用codesign之前。 (*或mkdir目标目录)

此外,在框架的文件夹上调用codesign,而不是在版本子文件夹上。

$ codesign --force --verify --verbose --sign "Developer ID Application: My ID" MyApplication.app/Contents/Frameworks/QtConcurrent.framework

希望这有帮助。

答案 1 :(得分:7)

<强>摘要

  1. 通过将Info.plist移动​​到框架中当前版本级别的Resources文件夹来修复格式错误的Qt框架。
  2. 使用codesign --deep选项一次签名包中的所有二进制文件,包括框架和插件。
  3. <强>详情

    解决此问题的最简单方法是使用--deep codesign选项。这个选项(我认为是Mavericks引入的)将签署目标包中的所有二进制文件,包括主应用程序二进制文件及其框架和插件。

    但是在使用此选项(或任何其他建议的签名技术)之前,您必须按照此线程上的其他帖子修复Qt框架。由于一些奇怪的原因,构建的Qt框架不符合Apple的框架规范,并且会混淆代码签名工具。这反过来可能导致不良行为,例如将框架的顶级符号链接替换为已签名框架二进制文件的附加副本。

    要修复Qt框架,您应该将Info.plist复制到特定版本框架中的Resources文件夹,而不是框架的顶层(如前所述)。此外,您可能希望在应用程序包框架中丢弃Info.plist的原始副本,因为它基本上位于错误的位置。

    具体来说,你应该做这样的事情来根据需要修复每个框架:

    mkdir -p MyKillerApp.app/Contents/Frameworks/QtCore.framework/Versions/Current/Resources
    cp lib/QtCore.framework/Contents/Info.plist MyKillerApp.app/Contents/Frameworks/QtCore.framework/Versions/Current/Resources
    rm -rf MyKillerApp.app/Contents/Frameworks/QtCore.framework/Contents
    

    以上假设框架中存在“Versions / Current”符号链接。我认为所有构建的Qt库都是这种情况。但是如果您的构建不是这种情况,那么您可以使用特定的版本文件夹名称(如“4”)而不是“当前”符号链接。

    在规范化框架之后,您可以使用--deep选项对应用程序包中的所有二进制文件进行签名:

    codesign --deep --force --verify --sign "Developer ID Application: My ID" MyKillerApp.app
    

    或者,如果您已在项目级别启用了代码签名以进行部署后处理,则可以在“其他代码签名标志”中传递--deep选项:

    OTHER_CODE_SIGN_FLAGS = --deep
    

    如果您对主应用程序的协同代码要求与对框架或插件的要求不同,则可以使用一些技巧。首先,使用frameworks / plugins要求使用--deep选项进行签名。然后,您可以使用--force选项再次签名,但不使用--deep选项,指定应用程序级别要求。结果将是主应用程序将使用应用程序要求进行签名,并且所有子二进制文件将使用框架/插件要求进行签名。

    这种方法可能被认为有点懒惰和浪费,因为您要对主应用程序签名两次。但它比从主应用程序二进制文件中分别查找和签署所有子二进制文件的方法更简单。

答案 2 :(得分:2)

非常感谢!

您还必须签署应用程序使用的所有插件

$ codesign --force --verify --verbose --sign "Developer ID Application: My ID" MyApplication.app/Contents/PlugIns/imageformats/libqgif.dylib

致以最诚挚的问候,

答案 3 :(得分:0)

我写了一个基于Benoît的答案的脚本,它完成了修补.app的工作: https://gist.github.com/kainjow/8059407

代码:

#!/usr/bin/env ruby

# Copies missing Info.plist files for a .app's Qt frameworks installed
# from macdeployqt. Without the plists, 'codesign' fails on 10.9 with
# "bundle format unrecognized, invalid, or unsuitable".
#
# Example usage:
# ruby macdeployqt_fix_frameworks.rb /Users/me/Qt5.2.0/5.2.0/clang_64/ MyProgram.app
#
# Links:
# https://bugreports.qt-project.org/browse/QTBUG-23268
# http://stackoverflow.com/questions/19637131/sign-a-framework-for-osx-10-9
# http://qt-project.org/forums/viewthread/35312

require 'fileutils'

qtdir = ARGV.shift
dotapp = ARGV.shift

abort "Missing args." if !qtdir || !dotapp
abort "\"#{qtdir}\" invalid" if !File.exists?(qtdir) || !File.directory?(qtdir)
abort "\"#{dotapp}\" invalid" if !File.exists?(dotapp) || !File.directory?(dotapp)

frameworksDir = File.join(dotapp, 'Contents', 'Frameworks')
Dir.foreach(frameworksDir) do |framework|
next if !framework.match(/^Qt.*.framework$/)
fullPath = File.join(frameworksDir, framework)
destPlist = File.join(fullPath, 'Resources', 'Info.plist')
next if File.exists?(destPlist)
srcPlist = File.join(qtdir, 'lib', framework, 'Contents', 'Info.plist')
abort "Source plist not found: \"#{srcPlist}\"" if !File.exists?(srcPlist)
FileUtils.cp(srcPlist, destPlist)
end 

答案 4 :(得分:0)

根据Apple docs,您需要签署版本文件夹,而不是框架本身。我试过了,遇到了两个问题。首先,Qt框架的一些Info.plist文件具有不正确的可执行文件名(_debug后缀)。在修复之后,我已经设法在所有框架上签署了“Apple方式”。但是,在这样做之后,我无法签署主应用程序,并且对我刚签署的Qt框架有错误。

因此,奇怪但有效的解决方案是签署框架文件夹。即使Info.plist文件中的可执行文件名不正确,也可以使用它。

答案 5 :(得分:0)

我在编码之前使用此脚本来纠正QT 4.8框架。希望这可以帮助。我花了大约3-4天的时间来解决这个问题(这是在QT的博客文章之前)。

# we need to massage qt frameworks so they get codesigned by the new codesign
# more information about this $#@$!@# piece of $@#$@$!
# http://blog.qt.digia.com/blog/2014/10/29/an-update-on-os-x-code-signing/

QT_LIB_DIR=$(qmake -query QT_INSTALL_LIBS)
for i in $(find "$APP/Contents/Frameworks/" -name Qt\*.framework); do
   FW_NAME=$(basename "$i")
   FW_SHORTNAME=$(basename -s ".framework" "$i")
   mv "$i/Resources" "$i/Versions/4"
   ln -s Versions/Current/Resources "$i/"
   ln -s Versions/Current/${FW_SHORTNAME} "$i/${FW_SHORTNAME}"
   ln -s 4 "$i/Versions/Current"
   cp "${QT_LIB_DIR}/${FW_NAME}/Contents/Info.plist" "$i/Resources"
   chmod u+w "$i/Resources/Info.plist"
   # now comes the real magic, we have to add CFBundleIdentifier and CFBundleVersion to the Info.plist
   awk "/<\/dict>/{print \"<key>CFBundleIdentifier</key>\n<string>org.qt-project.${FW_SHORTNAME}</string>\"}1" "$i/Resources/Info.plist" > "$i/Resources/Info.plist.tmp"
   mv "$i/Resources/Info.plist.tmp" "$i/Resources/Info.plist"
   awk '/<\/dict>/{print "<key>CFBundleVersion</key>\n<string>4.8</string>"}1' "$i/Resources/Info.plist" > "$i/Resources/Info.plist.tmp"
   mv "$i/Resources/Info.plist.tmp" "$i/Resources/Info.plist"
done