如何为Mac App Store共同设计框架捆绑包?

82

最近提交后,我遇到了以下错误:

无效签名-嵌套的应用程序包(FooBar.app/Contents/Frameworks/GData.framework)未经签名、签名无效或未使用苹果提交证书进行签名。有关更多信息,请参阅代码签名和应用程序沙箱指南。

无效签名-嵌套的应用程序包(FooBar.app/Contents/Frameworks/Growl.framework)未经签名、签名无效或未使用苹果提交证书进行签名。有关更多信息,请参阅代码签名和应用程序沙箱指南。

无效签名-嵌套的应用程序库libcurl(FooBar.app/Contents/Frameworks/libcurl.framework)未经签名、签名无效或未使用苹果提交证书进行签名。有关更多信息,请参阅代码签名和应用程序沙箱指南。

所以,我按照技术说明2206签署了所有框架包:

codesign -f -v -s "3rd Party Mac Developer Application: Name" ./libcurl.framework/Versions/A/libcurl
codesign -f -v -s "3rd Party Mac Developer Application: Name" ./libcurl.framework/Versions/A/libssh2.1.dylib
codesign -f -v -s "3rd Party Mac Developer Application: Name" ./Growl.framework/Versions/A/Growl
codesign -f -v -s "3rd Party Mac Developer Application: Name" ./GData.framework/Versions/A/GData

Technote 2206表示:

签署框架

由于框架是捆绑包,因此直接签署框架似乎是合乎逻辑的结论。但是,事实并非如此。为了在签署框架时避免问题,请务必签署特定版本而不是整个框架:

# 这是错误的方法:

codesign -s my-signing-identity ../FooBarBaz.framework

# 这是正确的方法:

codesign -s my-signing-identity ../FooBarBaz.framework/Versions/A

当我尝试验证结果时,结果看起来对我来说很好:

% codesign -vvv FooBar.app/Contents/Frameworks/libcurl.framework
FooBar.app/Contents/Frameworks/libcurl.framework: valid on disk
FooBar.app/Contents/Frameworks/libcurl.framework: satisfies its Designated Requirement
% codesign -vvv FooBar.app/Contents/Frameworks/Growl.framework
FooBar.app/Contents/Frameworks/Growl.framework: valid on disk
FooBar.app/Contents/Frameworks/Growl.framework: satisfies its Designated Requirement

为了好玩,我尝试直接签名框架捆绑包,但仍被拒绝。但这正是文档所说不要做的事情。

有没有猜测为什么会被认为是无效的?我正在使用与以前成功代码签名的相同证书。

我唯一的猜测是与现有的 plist(我需要拥有框架 Info.plist 中的标识符吗?)或授权有关 —— 有任何建议吗?


我之前提交我的应用程序时也发现了这个问题。幸运的是,苹果没有拒绝它,但指出我们以后必须签署框架。我认为最好在Growl谷歌代码问题页面上发布此问题,很快人们就会遇到同样的问题。 - koo
2
提交使用Growl框架的应用程序时,我也遇到了这个问题。我猜你需要将growl.framework包标识符更改为你自己拥有的标识符,然后对其进行代码签名。 - Andrew
这很奇怪:我发布了一个包含两个未签名框架(CorePlot和MacRuby)的应用程序。我只在应用程序捆绑包上运行了一次代码签名命令,而且应用程序被接受时没有对框架进行任何评论。现在,如果您查看应用程序捆绑包(http://bit.ly/charterapp),两个框架似乎都已签名。您是否尝试过简单地对整个应用程序进行签名? - p4010
@csexton,我还没有尝试过。在下一次更新中,我会再试试。需要注意的一件事是,在签名之后使用lipo(删除ppc支持)会使签名无效。 - Andrew
@csexton 脚本不包含.a文件的代码签名。我已经尝试在我的项目中使用它,但对于这种子组件而言,它失败了。我已经尝试修改它以便评估这些文件; 一切都很好,但(所有组件都已签名),但应用程序无法启动,显示无效签名错误。有什么想法吗?(注:我认为 entitlements 参数不再需要,并且它会停止在 XCode 5 上的任务) - danielemm
显示剩余3条评论
4个回答

50

基于baptr的答案,我开发了这个shell脚本来对所有我的框架和其他二进制资源/辅助可执行文件进行代码签名(目前支持的类型为:dylib、bundle和登录项):

#!/bin/sh

# WARNING: You may have to run Clean in Xcode after changing CODE_SIGN_IDENTITY! 

# Verify that $CODE_SIGN_IDENTITY is set
if [ -z "${CODE_SIGN_IDENTITY}" ] ; then
    echo "CODE_SIGN_IDENTITY needs to be set for framework code-signing!"

    if [ "${CONFIGURATION}" = "Release" ] ; then
        exit 1
    else
        # Code-signing is optional for non-release builds.
        exit 0
    fi
fi

if [ -z "${CODE_SIGN_ENTITLEMENTS}" ] ; then
    echo "CODE_SIGN_ENTITLEMENTS needs to be set for framework code-signing!"

    if [ "${CONFIGURATION}" = "Release" ] ; then
        exit 1
    else
        # Code-signing is optional for non-release builds.
        exit 0
    fi
fi

ITEMS=""

FRAMEWORKS_DIR="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -d "$FRAMEWORKS_DIR" ] ; then
    FRAMEWORKS=$(find "${FRAMEWORKS_DIR}" -depth -type d -name "*.framework" -or -name "*.dylib" -or -name "*.bundle" | sed -e "s/\(.*framework\)/\1\/Versions\/A\//")
    RESULT=$?
    if [[ $RESULT != 0 ]] ; then
        exit 1
    fi

    ITEMS="${FRAMEWORKS}"
fi

LOGINITEMS_DIR="${TARGET_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Library/LoginItems/"
if [ -d "$LOGINITEMS_DIR" ] ; then
    LOGINITEMS=$(find "${LOGINITEMS_DIR}" -depth -type d -name "*.app")
    RESULT=$?
    if [[ $RESULT != 0 ]] ; then
        exit 1
    fi

    ITEMS="${ITEMS}"$'\n'"${LOGINITEMS}"
fi

# Prefer the expanded name, if available.
CODE_SIGN_IDENTITY_FOR_ITEMS="${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
if [ "${CODE_SIGN_IDENTITY_FOR_ITEMS}" = "" ] ; then
    # Fall back to old behavior.
    CODE_SIGN_IDENTITY_FOR_ITEMS="${CODE_SIGN_IDENTITY}"
fi

echo "Identity:"
echo "${CODE_SIGN_IDENTITY_FOR_ITEMS}"

echo "Entitlements:"
echo "${CODE_SIGN_ENTITLEMENTS}"

echo "Found:"
echo "${ITEMS}"

# Change the Internal Field Separator (IFS) so that spaces in paths will not cause problems below.
SAVED_IFS=$IFS
IFS=$(echo -en "\n\b")

# Loop through all items.
for ITEM in $ITEMS;
do
    echo "Signing '${ITEM}'"
    codesign --force --verbose --sign "${CODE_SIGN_IDENTITY_FOR_ITEMS}" --entitlements "${CODE_SIGN_ENTITLEMENTS}" "${ITEM}"
    RESULT=$?
    if [[ $RESULT != 0 ]] ; then
        echo "Failed to sign '${ITEM}'."
        IFS=$SAVED_IFS
        exit 1
    fi
done

# Restore $IFS.
IFS=$SAVED_IFS
  1. 将其保存到项目中的文件中。我将我的副本保存在项目根目录下的Scripts子目录中。
    • 我的文件名为codesign-frameworks.sh
  2. 在“拷贝嵌入式框架”构建阶段之后添加一个“运行脚本”构建阶段。
    • 你可以称之为“Codesign Embedded Frameworks”。
  3. ./codesign-frameworks.sh(或上面脚本的名称)粘贴到脚本编辑器文本字段中。如果将脚本存储在子目录中,则使用./Scripts/codesign-frameworks.sh
  4. 构建你的应用程序。所有捆绑的框架都将被签名。

如果你仍然收到“Identity: ambiguous (matches: …”错误,请在下面评论。这不应再发生了。

更新于2012-11-14:为“codesign-frameworks.sh”添加对名称中包含特殊字符的框架的支持(不包括单引号)。

更新于2013-01-30:为“codesign-frameworks.sh”添加对所有路径中包含特殊字符的支持(这应该包括单引号)。

更新于2013-10-29:添加experimental dylib支持。

更新于2013-11-28:添加entitlements支持。改进了experimental dylib支持。

更新于2014-06-13:修复了包含(嵌套)框架的框架的代码签名问题。这是通过向find添加-depth选项来完成的,它会使find执行深度优先遍历。这变得必要,因为这里描述的问题。简而言之:只有当其嵌套的束已经签名时,才能签署包含束。

更新于2014-06-28:添加experimental bundle支持。

更新于2014-08-22:改进代码并防止无法恢复IFS。

更新于2014-09-26:添加对登录项的支持。

更新于2014-10-26:引用目录检查。这修复了包括特殊字符路径的“line 31/42: too many arguments”错误和由此导致的“ code object is not signed at all”错误。

更新于2014-11-07:在Xcode中使用自动身份验证解决模糊的identity错误(如“Mac Developer: ambiguous…”)。您不再需要显式设置identity,而只需使用“Mac Developer”即可!

更新于2015-08-07:改进了语义。

欢迎进行改进!


警告:更改CODE_SIGN_IDENTITY后,您可能需要在Xcode中运行Clean!

真是太棒了。每次我花费2个小时进行深入搜索,结果只需要进行一点清理。:(
- Git.Coach
1
Elmer Cat -- 我不知道你正在使用哪个版本的Xcode,但我正在使用5.0.1版本,它绝对不会自动签署我的框架。在找到这个脚本之前,我一直在抓狂。 - Bryan
我很想帮忙,@Bryan,但是我无法测试这个,因为我在任何地方都没有使用.dylib。你应该有足够的声望来获取我的脚本,按照你的喜好进行修改并在此处进行编辑! - JanX2
是的,但问题是当涉及到Bash脚本时,我完全脑死了。我不知道要添加什么,以便将任何带有*.dylib的文件添加到${Frameworks}中。 - Bryan
添加实验性dylib支持。 - JanX2
显示剩余13条评论

11

您的评论显示您签署了捆绑包版本目录中的对象。Technote 显示需要对目录本身进行签名。

以下内容更符合 Technote 的要求:

codesign -f -v -s "3rd Party Mac Developer Application: Name" ./libcurl.framework/Versions/A
codesign -f -v -s "3rd Party Mac Developer Application: Name" ./Growl.framework/Versions/A
codesign -f -v -s "3rd Party Mac Developer Application: Name" ./GData.framework/Versions/A

4

以下是我如何解决这个问题的方法:

  • 进入目标构建设置
  • 找到“其他代码签名标志”这一行
  • --deep值输入到发布参数中
  • 关闭Xcode
  • 进入您的Mac上的派生数据文件夹,并删除旧的派生数据(默认路径为:/Users/YOUR_USER_NAME/Library/Developer/Xcode/DerivedData)
  • 打开Xcode并进行构建

构建后,再次存档和提交应用程序...


5
在一个梦幻世界中,“-deep”可以如预期地工作,但我们并不生活在一个梦幻世界中... - mike

0
这里没有提到的一件事是,您需要将Info.plist放在版本化的框架目录内的/Resources中。否则,当您尝试对版本化的目录进行签名时,您将会收到“bundle format unrecognized, invalid, or unsuitable”错误。
我在这里提供了更详细的答案:如何为沙盒化的Mac应用程序Codesign Growl.framework

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接