5

I am having trouble signing Qt Based application un OS X. I am using Qt 5.3.2.

I have read various information source that contain contradictory information.

Here is the content of my application bundle after I run the bin/macdeployqt Qt utility

SimpleHello.app/
    Contents/
        Info.plist
        PkgInfo
        Frameworks/
            QtCore.framework/
                Resources/
                Versions/
                    5/
                        QtCore
            QtGui.framework/  ... same as Qt core
            QtPrintSupport.framework/  ... same as Qt core
            QtWidgets.framework/  ... same as Qt core
        MacOS/
            SimpleHello
        PlugIns/ ... some plugins
        Resources/
            empty.lproj
            qt.conf

First:

I tried: http://successfulsoftware.net/2012/08/30/how-to-sign-your-mac-os-x-app-for-gatekeeper/

However, it seems that it is not valid anymore in OS X 10.10 Yosemite

Second:

I tried: Sign a Framework for OSX 10.9

I was able to sign the whole application without any error. However, when running spctl to verify the validity of the application, I get

spctl -a -vvvv SimpleHello.app
SimpleHello.app/: rejected
source=obsolete resource envelope
origin=Developer ID Application: MY CERTIFICATE

Additionally when verifying signature with codesign, I get this:

codesign --verify --deep --verbose=4 SimpleHello.app
--prepared:/My/Path/SimpleHello.app/Contents/Frameworks/QtCore.framework
--validated:/My/Path/SimpleHello.app/Contents/Frameworks/QtCore.framework
SimpleHello.app/: embedded framework contains modified or invalid version
In subcomponent: /My/Path/SimpleHello.app/Contents/Frameworks/QtCore.framework

Third:

Added the --no-strict option in codesign verification according to: Error when export archive

It fixes the issue with codesign verification but does not fix the spctl issue.

Forth:

I tried adding the --no-legacy-signing option when signing frameworks. However I get this error when verifying the bundle signature (both with codesign and spctl

codesign --verify --deep --verbose=4 SimpleHello.app
SimpleHello.app/: code has no resources but signature indicates they must be present

Fifth:

Modified the framework structure according to: http://qt-project.org/forums/viewthread/47768 and https://gist.github.com/kingcheez/6154462d7734e0c0f3a4

In this case I get this error when trying to sign frameworks

SimpleHello.app/Contents/Frameworks/QtCore.framework: unsealed contents present in the root directory of an embedded framework
SimpleHello.app/Contents/Frameworks/QtGui.framework: unsealed contents present in the root directory of an embedded framework
SimpleHello.app/Contents/Frameworks/QtPrintSupport.framework: unsealed contents present in the root directory of an embedded framework
SimpleHello.app/Contents/Frameworks/QtWidgets.framework: unsealed contents present in the root directory of an embedded framework

EDIT: It seems that the issue with the unsealed contents present in the root directory of an embedded framework was because one of the simlink was malformed. It was:

QtCore.framework.framework/Versions/Current -> 5/

Instead of

QtCore.framework.framework/Versions/Current -> 5

After this fix, I still get the same result as in Sixth though.

Sixth:

Added the --no-strict option when calling codesign for Frameworks. I was able to sign all frameworks except for one

SimpleHello.app//Contents/Frameworks/QtCore.framework: signed bundle with Mach-O thin (x86_64) [.]
SimpleHello.app//Contents/Frameworks/QtGui.framework: signed bundle with Mach-O thin (x86_64) [.]
SimpleHello.app//Contents/Frameworks/QtPrintSupport.framework: code object is not signed at all
In subcomponent: /My/Path/SimpleHello.app/Contents/Frameworks/QtPrintSupport.framework/Versions/Current/QtPrintSupport
SimpleHello.app//Contents/Frameworks/QtWidgets.framework: signed bundle with Mach-O thin (x86_64) [.]

Seventh:

I posted this question since I don't know what to look for anymore

Community
  • 1
  • 1
gfrigon
  • 2,237
  • 2
  • 23
  • 32

2 Answers2

4

After digging a bit more, I figured out what the issue in section seventh was: Some of the Qt Framework contain bad information in the Info.plist files (framework name ends with _debug)

I came out with this script that fixes all issues (there are still a few hardcoded values that could probably get processed with some improvement to the script)

#!/bin/bash
# Script name: deploy.sh

# Following environment variables must be defined:
# - QT_FRAMEWORK_PATH
# - QT_BIN_PATH
# - CERTIFICATE
# - FRAMEWORKS
# - BAD_FRAMEWORKS


# retrieve bundle name from first parameter
BUNDLE_NAME=$1

# Run QT tool to deploy
${QT_BIN_PATH}/macdeployqt $BUNDLE_NAME

# FIX ISSUE 6
# Please note that Qt5 frameworks have incorrect layout after SDK build, so this isn't just a problem with `macdeployqt` but whole framework assembly part.
# Present
#   QtCore.framework/
#       Contents/
#           Info.plist
#       QtCore    -> Versions/Current/QtCore
#       Versions/
#           Current -> 5
#           5/
#               QtCore
# After macdeployqt
#   QtCore.framework/
#       Resources/
#       Versions/
#           5/
#               QtCore
#
# Expected
#   QtCore.framework/
#       QtCore    -> Versions/Current/QtCore
#       Resources -> Versions/Current/Resources
#       Versions/
#           Current -> 5
#           5/
#               QtCore
#               Resources/
#                   Info.plist
# So in order to comply with expected layout: https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html

for CURRENT_FRAMEWORK in ${FRAMEWORKS}; do
    echo "Processing framework: ${CURRENT_FRAMEWORK}"

    echo "Deleting existing resource folder"
    rmdir ${BUNDLE_NAME}/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework/Resources

    echo "create resource folder"
    mkdir -p ${BUNDLE_NAME}/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework/Versions/5/Resources

    echo "create copy resource file"
    cp ${QT_FRAMEWORK_PATH}/${CURRENT_FRAMEWORK}.framework/Contents/Info.plist $BUNDLE_NAME/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework/Versions/5/Resources/

    echo "create symbolic links"
    ln -nfs 5                                     ${BUNDLE_NAME}/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework/Versions/Current
    ln -nfs Versions/Current/${CURRENT_FRAMEWORK} ${BUNDLE_NAME}/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework/${CURRENT_FRAMEWORK}
    ln -nfs Versions/Current/Resources            ${BUNDLE_NAME}/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework/Resources
done

# FIX ISSUE 7
echo "***** Correct Frameworks Info.plist file*****"

for CURRENT_FRAMEWORK in ${BAD_FRAMEWORKS}; do
    echo "Correcting bad framework Info.plist: ${CURRENT_FRAMEWORK}"
    TMP=$(sed 's/_debug//g' ${BUNDLE_NAME}/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework/Resources/Info.plist)
    echo "$TMP" > ${BUNDLE_NAME}/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework/Resources/Info.plist
done

# SIGNING FIXED FRAMEWORK
CODESIGN_OPTIONS="--verbose=4"

#echo "******* Sign QtWebEngineProcess ***********"
#codesign --force --verify ${CODESIGN_OPTIONS} --sign "$CERTIFICATE" $BUNDLE_NAME/Contents/Frameworks/QtWebEngineCore.framework/Versions/Current/Helpers/QtWebEngineProcess.app
echo "******* Sign Frameworks-subApps ***********"
codesign --force --verify ${CODESIGN_OPTIONS} --sign "$CERTIFICATE" $BUNDLE_NAME/Contents/Frameworks/*.framework/Versions/*/*/*.app

echo "******* Signing Frameworks ***********"
for CURRENT_FRAMEWORK in ${FRAMEWORKS}; do
    echo "Signing framework: ${CURRENT_FRAMEWORK}"
    codesign --force --verify ${CODESIGN_OPTIONS} --sign "$CERTIFICATE" $BUNDLE_NAME/Contents/Frameworks/${CURRENT_FRAMEWORK}.framework
done

# Sign plugins
echo "******* Signing Plugins ***********"
codesign --force --verify ${CODESIGN_OPTIONS} --sign "${CERTIFICATE}" ${BUNDLE_NAME}/Contents/Plugins/*/*.dylib


# Sign bundle itself
echo "******* Signing Bundle ***********"
codesign --force --verify ${CODESIGN_OPTIONS} --sign "$CERTIFICATE" $BUNDLE_NAME

# Verify

echo "******* Verify Bundle ***********"
codesign --verify --deep ${CODESIGN_OPTIONS} $BUNDLE_NAME


echo "******* Verify Bundle using dpctl ***********"
spctl -a -vvvv $BUNDLE_NAME

As for calling the script:

# Define environment variables
export QT_FRAMEWORK_PATH=/Path/To/Qt_5.3.2/5.3/clang_64/lib
export QT_BIN_PATH=/Path/To/Qt_5.3.2/5.3/clang_64/bin
export CERTIFICATE="Developer ID Application: My Certificate"
export FRAMEWORKS="QtCore QtGui QtPrintSupport QtWidgets"
export BAD_FRAMEWORKS="QtPrintSupport"

# Call itself
deploy.sh SimpleHello.app

With this script, the final output is:

SimpleHello.app/: accepted
source=Developer ID
origin=Developer ID Application: My Certificate (HASH)
karololszak
  • 88
  • 1
  • 6
gfrigon
  • 2,237
  • 2
  • 23
  • 32
  • Great man! Finally a working solution. Don't forget to use cp -R to copy the app in eventual scripts otherwise signature is broken. – Indio Jul 29 '15 at 07:43
0

I run a script similar to this after running macdeployqt:

#!/bin/bash

#copy .plist files to frameworks
cp "/usr/local/Trolltech/Qt-4.8.5/lib/QtCore.framework/Contents/Info.plist" "SimpleHello.app/Contents/Frameworks/QtCore.framework/Resources/Info.plist"
#copy folders to proper location
cp -r "SimpleHello.app/Contents/Frameworks/QtCore.framework/Resources" "SimpleHello.app/Contents/Frameworks/QtCore.framework/Versions/4/Resources"
#delete old folders
rm -rf "SimpleHello.app/Contents/Frameworks/QtCore.framework/Resources"
#create symlinks
ln -s "Versions/4/Resources" "SimpleHello.app/Contents/Frameworks/QtCore.framework/Resources"

After, I use:

codesign --deep -f -s

and it works, just add the missing frameworks in a similar fashion. I haven't tried it with qt 5+ but it might work for it to.

GlassFish
  • 14,851
  • 3
  • 17
  • 22
  • Unfortunately, this does't fix all the issues. It seems that , for some Qt 5+ Frameworks, Info.plist contain errors that need to be corrected before signing. I am also surprised that your solution works since with OS X 10.9+ there needs to be 3 symlinks in the Framework. I have posted the solution that finally worked for me. – gfrigon Jan 22 '15 at 19:36
  • @gfrigon I literally just copied one line of each step and replaced my app name with what you used. When I made the script I was doing allot of trial and error, I never had to add the 2 more symlinks, I'm using Xcode 6.0.1 – GlassFish Jan 23 '15 at 07:54
  • Are you using OS X 10.10? It seems that this version is pickier than earlier versions of framework structure. I have re-tested your solution, just to make sure (adding the correction of bad info.plist file in Qt 5.3.2 frameworks), and it doesn't succeed Gatekeeper verifications. Thanks for the help and feedback though. – gfrigon Jan 28 '15 at 15:57