Until the introduction of Gatekeeper in Mac OS X 10.8, the concept of code signing (introduced in Mac OS X 10.5) was not so well discussed in the Mac developer community. Gatekeeper is a security feature introduced by Apple to ensure that Mac apps coming only from either App Store or from Registered Apple Developers (via their websites) are allowed to run on a User’s Mac by default. Users have the option of changing Gatekeeper settings at their own risk to allow apps from any source to run but most users don’t exercise that option.
Gatekeeper requires apps to be properly code signed. An app is considered properly code signed if the main executable and all the nested bundles (embedded frameworks, libraries, helper executables, plug-ins etc) contained in it are properly code signed.
Xcode on Mac OS X Maverics doesn’t allow you to sign a bundle if any nested bundle in it is unsigned. These nested bundles could be things like helper executables, libraries, embedded frameworks, plug-ins, XPC services. Therefore
- You should sign the unsigned bundles with your own code signing identity i.e. your Developer Id Certificate or App Store Distribution Certificate.
- It is recommended that you re-sign the bundles that are already signed by third parties with your own code signing identity i.e. your Developer Id Certificate or App Store Distribution Certificate.
Code Signing Bundles The Wrong Way
Some googling would lead you to a “quick” fix on the internet to recursively sign all the nested bundles in your app. The “quick” fix is to use codesign’s –deep option in “Other Code Signing Flags” in your target’s build settings. But this solution is incorrect due to the fact that while recursively signing the nested bundles, –deep applies top-level bundle parameters like your app’s Entitlements to all the nested bundles rendering them invalid.
When users download and install such an app, Gatekeeper will notify them that it is damaged and cannot be opened. If you check the app using command line, here is what you get
$ spctl --verbose=4 --assess --type execute /Users/Sayeed/Myapp.app
/Users/Sayeed/Myapp.app: a sealed resource is missing or invalid
Code Signing Bundles The Right Way
The right way to code sign nested bundles is to add another Build Phase to your target.
When you embed frameworks, you add a “Copy Files” build phase to copy them into the Frameworks folder in your app bundle. This enables the app to run on the users’ systems because these frameworks don’t ship by default on Mac OS X. To code sign embedded frameworks after they are copied, add a Run Script Build Phase from Xcode > Editor > Add Build Phase. The Shell is bash (/bin/sh) and the script looks like
IDENTITY="3rd Party Mac Developer Application: Sayeed Hussain (83YFQ426C5)"
codesign --verbose --force --sign "$IDENTITY" "$LOCATION/Coreplot.framework/Versions/A"
codesign --verbose --force --sign "$IDENTITY" "$LOCATION/CrashReporter.framework/Versions/A"
#other embedded frameworks
Important: Frameworks are “versioned bundles”, and each contained version should be signed and validated separately. For example:
$ codesign -s my-signing-identity ../MyCustomFramework/Versions/A
$ codesign -s my-signing-identity ../MyCustomFramework/Versions/B
All embedded frameworks need to be signed separetly by adding the codesign command in the shell script.
If you are bulding to distribute outside of the Mac App Store, you should use your “Developer ID Application” code signing identity. If you are building for the Mac App Store, you should use your “3rd Party Mac Developer Application” code signing identity. Other embedded bundles like helper executables, plug-ins or XPC services, need to be signed appropriately after being copied into the app bundle.
Go ahead and code sign your embedded frameworks the right way. That’s it for now. Good Luck And Good Life!!!