How do you codesign framework bundles for the Mac App Store?
Asked Answered
S

4

82

After a recent submission I have gotten the following error:

Invalid Signature - the nested app bundle (FooBar.app/Contents/Frameworks/GData.framework) is not signed, the signature is invalid, or it is not signed with an Apple submission certificate. Refer to the Code Signing and Application Sandboxing Guide for more information.

Invalid Signature - the nested app bundle (FooBar.app/Contents/Frameworks/Growl.framework) is not signed, the signature is invalid, or it is not signed with an Apple submission certificate. Refer to the Code Signing and Application Sandboxing Guide for more information.

Invalid Signature - the nested app bundle libcurl (FooBar.app/Contents/Frameworks/libcurl.framework) is not signed, the signature is invalid, or it is not signed with an Apple submission certificate. Refer to the Code Signing and Application Sandboxing Guide for more information.

So I signed all the framework bundles per Technote 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 says:

Signing Frameworks

Seeing as frameworks are bundles it would seem logical to conclude that you can sign a framework directly. However, this is not the case. To avoid problems when signing frameworks make sure that you sign a specific version as opposed to the whole framework:

# This is the wrong way:

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

# This is the right way:

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

And when I try to verify the results, it looks good to me:

% 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

For fun, I did try signing the framework bundle directly and it was still rejected. But that is exactly what the documentation said not to do.

Any guesses why that would be considered invalid? I am using the same cert that I use to code sign my app -- the one that has worked in the past.

My only guess would be something to do with the existing plists (do I need to own the identifiers in the framework's Info.plists?) or entitlements -- any suggestions?

Square answered 8/10, 2011 at 14:37 Comment(8)
I as well discovered this earlier when I submitted my app. Thankfully Apple did not reject it but noted that we will have to sign frameworks later. I think it is better to post on the Growl google code issues page and very soon people are going to bump into the same problem.Caliber
Also ran into this problem when submitting an app with the Growl framework. I'm guessing that you'll have to change the growl.framework bundle identifier to one you own and then codesign it.Mourn
This is strange: I have published an app, which includes two frameworks (CorePlot and MacRuby), both unsigned. I only run the code sign command on the app bundle once, and the app has been accepted without any comment on framework. Now if you look in the app bundle (bit.ly/charterapp), both framework appear to be signed. Did you try to simply sign the whole app?Rikki
@p4010, I did -- they were previously unsigned. Growl for example was just the straight bundle they distribute. Now, I have had this app in the store for a while, so I assume this has to do with the new sandboxing stuff. When did you submit your app?Square
@Andrew, did changing the bundle identifier work for you?Square
@csexton, I didn't try it yet. On the next update I'll monkey around with it some more. One thing to watch out for would be using lipo (to remove ppc support) after codesigning since that would invalidate the signature.Mourn
I correct myself: I just uploaded a new version of my app, which has been accepted but with a warning email regarding missing/invalid signatures on included frameworks. Apparently, this is a recent development in app review (my former version included the same unsigned frameworks and I never had such a warning). @csexton: my previous submission was on Oct 6th.Rikki
@Square Script don't include code sign for .a files. I've tried to use it in my project and it fails for this kind of subcomponent. I've tried to modify it in order to evaluate these files; everything is fine but (all components are signed) but the app won't launch with invalid sign error. Ideas? (Note: I think entitlements params is no longer needed and it stops tasks on XCode 5)Coracle
P
51

Based on baptr’s answer, I have developed this shell script that codesigns all my frameworks and other binary resources/auxiliary executables (currently supported types: dylib, bundle, and login items):

#!/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. Save it to a file in your project. I keep my copy in a Scripts subdirectory in my project’s root.
    • Mine is called codesign-frameworks.sh.
  2. Add a “Run Script” build phase right after your “Copy Embedded Frameworks” build phase.
    • You can call it “Codesign Embedded Frameworks”.
  3. Paste ./codesign-frameworks.sh (or whatever you called your script above) into the script editor text field. Use ./Scripts/codesign-frameworks.sh if you store the script in a subdirectory.
  4. Build your app. All bundled frameworks will be codesigned.

Should you still get an “Identity: ambiguous (matches: …” error, please comment below. This should not happen anymore.

Updated 2012-11-14: Adding support for frameworks with special characters in their name (this does not include single quotes) to “codesign-frameworks.sh”.

Updated 2013-01-30: Adding support for special characters in all paths (this should include single quotes) to “codesign-frameworks.sh”.

Updated 2013-10-29: Adding experimental dylib support.

Updated 2013-11-28: Adding entitlements support. Improving experimental dylib support.

Updated 2014-06-13: Fixing codesigning issues with frameworks containing (nested) frameworks. This was done by adding -depth option to find, which causes find to do a depth-first traversal. This has become necessary, because of the issue described here. In short: a containing bundle can only be signed if its nested bundles are signed already.

Updated 2014-06-28: Adding experimental bundle support.

Updated 2014-08-22: Improving code and preventing failure to restore IFS.

Updated 2014-09-26: Adding support for login items.

Updated 2014-10-26: Quoting directory checks. This fixes the “line 31/42: too many arguments” errors and the resulting “ code object is not signed at all” error for paths including special characters.

Updated 2014-11-07: Resolving the ambiguous identity error (like “Mac Developer: ambiguous …”) when using automatic identity resolution in Xcode. You don’t have to explicitly set the identity anymore and can just use “Mac Developer”!

Updated 2015-08-07: Improving semantics.

Improvements welcome!

Phosphide answered 1/7, 2012 at 18:1 Comment(18)
Thanks for this, just a note though, it doesn't work if your target has a space in it's name. I tried to fix but couldn't get it to work so changed the target.Punctate
Should work now when the FRAMEWORK_DIR has spaces/special characters in it’s name.Phosphide
However, I also needed to disable the timestamp option by adding "--timestamp" to the end of the codesign command.Scribner
Interesting. Are you @Elmer Cat, by chance, doing this on an unreleased version of OS X? The man page describes this option of having “system-specific default behavior”.Phosphide
Checking again, @JanX2, the need to do this at all seems to have been eliminated by a newer version of Xcode that code signs the frameworks directly.Scribner
# WARNING: You may have to run Clean in Xcode after changing CODE_SIGN_IDENTITY! Pure Gold. Everytime I spent 2 hours of indepth search it is just a little clean up. :(Hard
Jan - Can you update this to sign *.dylib frameworks as well? Those get copied to the same "Frameworks" folder of the built application bundle, but they don't have the subfolders (/Versions/A) etc; they're just single files that end in .dylib. Each needs to be signed as well.Angellaangelle
Elmer Cat -- I don't know what version of Xcode you're running, but I'm on 5.0.1 and it is definitely NOT signing my frameworks automatically. I was tearing my hair out until I found this script.Angellaangelle
I would love to @Bryan, but I can’t test this as I don’t use .dylibs anywhere I can think of. You should have enough reputation to take my script, modify it to your taste and edit it here!Phosphide
Yep, but the problem is that I'm completely brain-dead when it comes to Bash scripts. I have no idea what to add so that any file with *.dylib is added to ${Frameworks}.Angellaangelle
Adding experimental dylib support.Phosphide
JanX2 - this doesn't work for dylibs, but the fix is easy - change your sed command to something like: sed -e "s/\(.*framework\)/\1\/Versions\/A\//"Borst
Thanks @Adrian! Just added that and some other stuff.Phosphide
Perfect!! Yosemite demands to sign all frameworks on a signed app. I my case, I'm distributing outside the Mac App Store, so no need for entitlements.Beatific
@Phosphide thanks for this! However I have a helper app in my OS X app (written in Swift) and this does not resign the frameworks inside the helpers. I tried rewriting this script to sign them, but I still had to resign them from the terminal (after archiving) for Apple to accept it.Kurman
@Kurman You can swap FRAMEWORKS_FOLDER_PATH for CONTENTS_FOLDER_PATH for the FRAMEWORK_DIR. This should ensure that absolutely all frameworks are codesigned. Please post your results. :)Phosphide
@Phosphide I've tried a few things and it simply doesn't work. It is definitely code signing the framework files, but after submission I get an email from Apple saying they are not. If I manually sign them after the archive build completes, Apple accepts the build.Kurman
@Kurman can you post an example steps to manually sign after the archive build completes? I'm having issues with my enterprise signing on my own app.Matterhorn
N
11

Your comment shows you signed the objects within the bundle's version directory. The Technote shows to sign the directory itself.

The following matches the Technote better:

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
Nesto answered 2/12, 2011 at 7:47 Comment(0)
B
4

This is how I fixed it;

  • Enter to the build settings of your target
  • Find the line "Other Code Signing Flags"
  • Enter --deep value to the release parameter
  • Close Xcode
  • Enter to the derived data folder on your Mac and delete the old derived data (default path is: /Users/YOUR_USER_NAME/Library/Developer/Xcode/DerivedData)
  • Open Xcode and build

After the build archive and submit the app again...

Bonanno answered 11/9, 2015 at 12:20 Comment(1)
In a dream world "--deep" would work as expected but we are not living in a dream world...Quern
K
0

One thing I don't see mentioned here is that you need to have your Info.plist inside /Resources inside the versioned framework directory. Otherwise you'll get the "bundle format unrecognized, invalid, or unsuitable" error when you try to sign the versioned directory.

I provided a more extended answer here: How to Codesign Growl.framework for Sandboxed Mac App

Kalgan answered 9/8, 2013 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.