An update on OS X Code Signing

There has recently been updates to the OS X code signing process. These updates also affect Qt applications signed for distribution, both on and outside the App Store.

OS X 10.9 Mavericks introduced version 2 signatures. As of OS X 10.9.5 and 10.10 Yosemite, v2 signatures are now required. We've recently spent some time updating Qt to be v2 compliant and Qt 5.4 will be the first compliant release. This includes patches to qmake and the macdeployqt deployment utility. Manually correcting the signing errors is possible if you are using an older version of Qt.

The Apple documentation is quite comprehensive on this topic, in particular see Code Signing Guide and TN2206 OS X Code Signing In Depth. The Qt tracking bug for this issue is QTBUG-32896.

Required changes to Qt

The required changes falls in three categories: updating the framework and application bundle structure, updating Info.plist contents, and special Qt Quick imports handling.

Updating the framework and application bundle structure

  • The Info.plist file must be placed in Versions/5/Resources/Info.plist.
  • Updated symlink structure: “Current” symlink points to the actual version (“4” or “5”).
  • The framework must be “clean” at code signing time, with for example no QtCore.prl at the root. This is currently enforced by macdeployqt.

As an example, the QtCore.framework structure should look like this:

QtCore.framework/
QtCore -> Versions/Current/QtCore
Resources -> Versions/Current/Resources
Versions/
Current -> 5
5/
QtCore
Resources/
Info.plist

And a typical app bundle:

foo.app/
Contents/
Frameworks/
QtCore.framework/
Info.plist
MacOS/
foo
PkgInfo/
PlugIns/
Resources/

Update Info.plist contents
The Info.plist files as generated by current qmake versions are missing some required keys:

  • CFBundleIdentifier ("org.qt-project.QtCore")
  • CFBundleVersion ("5.4.0")

Split Qt Quick imports into code and data in the application bundle

This is a v2 code signing requirement which affects applications with Qt Quick imports that contain both binary code (.dylib) and data (.qml) files.

Previous versions macdeployqt would deploy all files to Resources/, but as of v2 binary code in Resources/ is now prohibited. This is solved deploying .dylib files to PlugIns/ and then placing a symlink in Resources/ pointing to the .dylib.

Code signing flow and checkpoints

The following outlines a typical code signing process, with notes on where a current 4.8/5.3 Qt install may fail. The focus is on using the command line tools. Code signing using Xcode is possible but not covered here. The process is incremental: Some steps can be verified by any developer locally, while others require developer account admin access.

1) Signing and verifying the app bundle.

    sign: codesign --deep foo.app -s MyCertificate
or: macdeployqt foo.app -codesign=MyCertificate
verify: codesign —verify foo.app

The -deep option signs the app bundle recursively, including contained frameworks. While convenient to use, --deep is documented for “emergency repairs and temporary adjustments only“. As of Qt 5.4 macdeployqt has a -codesign option that recursively signs the app bundle without using --deep.

Common errors at this stage include “bundle format unrecognized, invalid, or unsuitable”, and/or “bar.dylib: code object is not signed at all”, which indicates that the framework structure is not correct or that some contained binary was not signed.

At this point which certificate you use does not matter. You can use a self-signed one created in Keychain Access for development and testing purposes.

2a) Distribution outside the App Store
Verify that GateKeeper will allow the app:

    spctl --assess --type execute foo.app

spctl outputs nothing on success. Common errors include “Rejected”. The bundle must be signed with the correct "Developer ID Application" production certificate for this check to work. The certificate is available for download to the Team Agent in the Mac Dev Center.

2b) App Store distribution
May trigger additional errors:

    ERROR ITMS-9000 The application bundle contains a tool or framework
foo.app/Contents/Frameworks/QtCore.framework that is missing the
bundle identifier.

This is currently a pending task for Qt 5.4. You can edit the plist files manually and add a bundle identifiers.

Availability

The updates are/will be available for three Qt versions:

  • 5.3 source code (not packaged)
  • 5.4 release. The updates will be be a part of the 5.4 source and binary release.
  • 4.8 patches: https://codereview.qt-project.org/#/c/95572, with the intention that this will be a part of a future 4.8.x release.

TODO list

The fixes for some issues are currently in progress:

  • Adding CFBundleIdentifier for the Qt frameworks
  • Changing the location of .prl files

Do you have corrections or additional info? Hit the comments section!


Blog Topics:

Comments