16

First, a little background: I'm investigating why my company's MacOS/X application (which by all accounts appears to be correctly signed; it runs fine under MacOS/X 10.11.x and 10.12.x; Gatekeeper is fine with it on all MacOS versions; "spctl --assess", and "codesign -vvvv" all say it satisfies its requirement on all OS versions) nevertheless won't launch under OS/X 10.10.x -- under 10.10.x when I try to launch it, I get a Crash Report where dyld complains that some of the libraries aren't signed correctly:

Dyld Error Message:
  Library not loaded:     @executable_path/../Frameworks/libcrypto.1.0.0.dylib
  Referenced from: /Applications/MyApplication v123/MyApplication.app/Contents/MacOS/MyApplication
  Reason: no suitable image found.  Did find:
  /Applications/MyApplication v123/MyApplication.app/Contents/MacOS/../Frameworks/libcrypto.1.0.0.dylib: code signature invalid for '/Applications/MyApplication v123/MyApplication.app/Contents/MacOS/../Frameworks/libcrypto.1.0.0.dylib'

While investigating that problem, I noticed that the libraries in the .app/Contents/Framework -- which are all signed using the exact same codesign command, via the build/package script on our OS/X build machine running OS/X 10.12 -- have differing kinds of hashes computed for them.

That is, if I look at how one of the non-Qt .dylib files was signed, I see it has only a sha256 hash recorded in it:

sierrabuild-polaris:MyApp v123 autobuild$ codesign -vvvd ./MyApp.app/Contents/Frameworks/libsndfile.1.dylib 
Executable=/Applications/MyApp v123/MyApp.app/Contents/Frameworks/libsndfile.1.dylib
Identifier=libsndfile.1
Format=Mach-O thin (x86_64)
CodeDirectory v=20200 size=4140 flags=0x0(none) hashes=125+2 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha256=b4256e9bf0fac567bb8ac86f56964c066b93d069
Hash choices=sha256     <----------------------------- ONLY 256!?
CDHash=b4256e9bf0fac567bb8ac86f56964c066b93d069
Signature size=8846
Authority=Developer ID Application: MyCompany
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Jan 24, 2017, 1:39:58 AM
Info.plist=not bound
TeamIdentifier=5XD27G7646
Sealed Resources=none
Internal requirements count=1 size=172

... but if I look at how any of the captive Qt frameworks was signed, OTOH, I see it has both sha1 and sha256 hashes included:

sierrabuild-polaris:MyApp v123 autobuild$ codesign -vvvd ./MyApp.app/Contents/Frameworks/QtCore.framework/Versions/5/QtCore
Executable=/Applications/MyApp v123/MyApp.app/Contents/Frameworks/QtCore.framework/Versions/5/QtCore
Identifier=org.qt-project.QtCore
Format=bundle with Mach-O thin (x86_64)
CodeDirectory v=20200 size=42549 flags=0x0(none) hashes=1324+3 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha1=09b5854f83091228f1baaad1455e7a30d6500c95
CandidateCDHash sha256=6dfdc74da06618e1b406a6e5fd0794fe43701def
Hash choices=sha1,sha256    <------------- BOTH sha1 and sha256, yay!
CDHash=6dfdc74da06618e1b406a6e5fd0794fe43701def
Signature size=8896
Authority=Developer ID Application: MyCompany
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Jan 24, 2017, 1:39:57 AM
Info.plist entries=8
TeamIdentifier=5XD27G7646
Sealed Resources version=2 rules=13 files=1
Internal requirements count=1 size=184

Given that dyld's error when trying to run my app under Yosemite always refers to one of the libraries that only has a sha256 hash, my working theory is that OS/X 10.10.x's dyld is ancient enough that it doesn't know about SHA-256 hashes, and that is why it is erroring out when it tries to load a captive shared library that is signed only with an SHA-256 hash.

My question (assuming I'm not completely barking up the wrong tree here) is: how does codesign decide when to stamp a file with sha256 hash alone, vs adding both an sha1 and an sha256 hash? And how can I force codesign to always include both hashes, so that my app can launch under 10.10.x again (like it used to before we upgraded our build machine to OSX/Sierra)?

For the record, here is how I'm invoking codesign in my build script -- the invocation arguments are exactly the same for all libraries (both the Qt framework libraries that end up with sha1,sha256 and the non-Qt libraries that end up with only sha256), e.g.:

codesign -f -v -s "Developer ID Application:  MyCompanyName" "./Frameworks/libcrypto.1.0.0.dylib"
codesign -f -v -s "Developer ID Application:  MyCompanyName" "./Frameworks/QtCore.framework/Versions/5/QtCore"
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234

3 Answers3

19

After a lot of googling around, this answer and this answer led me to the solution.

The problem was that several of the third-party shared libraries included inside my app were being compiled using just their default build settings (e.g. "./configure; make"), and since they were being compiled under OS/X 10.12, naturally they were compiled with only 10.12-compatibility in mind.

In order to get them to compile in such a way that the resulting .dylib files would be appropriate for earlier OS/X versions as well, I added these lines to the top of my build script:

export  LDFLAGS="-mmacosx-version-min=10.9"   
export   CFLAGS="-mmacosx-version-min=10.9"   
export CXXFLAGS="-mmacosx-version-min=10.9"

... and that did the trick for all of the libraries (libssh2, libsndfile, libogg, libflac, libvorbis, etc) except for the libssl -- for that one I had to hand-modify the Configure file and insert the -mmacosx-version-min argument into the compiler's command-line arguments that way.

With that change, codesign now applies both SHA-1 and SHA-256 hashes to all of the .dylib files, and the resulting .app now runs as expected under 10.10.x.

Community
  • 1
  • 1
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • Hello I'm currently facing the same issue, and something is not clear for me Did you have to add your export line at the top of your script used to build your own app, or did you add those export lines in command line, before rebuilding the 3rd party libs like libsndfile ? – nmud Jun 06 '17 at 13:40
  • I've got a top-level build script that calls down to lower-level build-scripts that build the libraries, and then a final lower-level build-script that builds my app that uses those libraries. I put the above export-commands at the top of this top-level build-script so that the LDFLAGS, CFLAGS, and CXXFLAGS environment variables are present in the environment for all of the build scripts (libraries and app). – Jeremy Friesner Jun 06 '17 at 14:35
  • Alright. In my case, I'm building a Qt app, so I would have to rebuild the 3rd party libs ahead with those exported flags, and then rebuild my own app once the libs are ok. Thing is I've built 3rd party libs like libsndfile with brew, so I don't know if I can do something like ./configure CFLAGS="..." or not – nmud Jun 06 '17 at 14:46
  • I found that I had to avoid using brew in order to get this issue sorted out :( – Jeremy Friesner Jun 06 '17 at 17:44
  • Indeed, I think I'll have to build every lib again without brew so I have more control. Thanks for the help ! – nmud Jun 07 '17 at 07:55
  • Hey, sorry to bother you again, I just have a question regarding openssl. How exactly did you edit the configure script for openssl ? This file is really huge and i'm not really a pro when it comes to customizing builds ! – nmud Jun 13 '17 at 14:00
  • Dunno if it's the best way to do it, but what I did was edit line 554 of the Configure script in the openssl root-folder (the one that starts with: "darwin64-x86_64-cc","cc:-arch x86_64 ... and inserted -mmacosx-version-min=10.9 into that line, just before the -O3 argument. – Jeremy Friesner Jun 13 '17 at 14:23
  • You're a hero. :') – NHDaly May 10 '18 at 20:27
  • Helped me as well. Specifying CFLAGS appeared to be enough. Thank you! – julia_v Feb 14 '20 at 09:24
2

Jeremy Friesner's answer worked for me. Just a side note on compiling OpenSSL. At least for 1.0.2h there was no need to change Configure file. The following worked fine

./configure darwin64-x86_64-cc shared --openssldir=$HOME/cmake_builds/openssl-1.0.2h.bin -mmacosx-version-min=10.10
hjpotter92
  • 78,589
  • 36
  • 144
  • 183
0

It stops using SHA-1 only if you target macOS 10.14 or later.

Setting MACOSX_DEPLOYMENT_TARGET = 10.14 worked for me (needs to be set as an environment variable for building and linking outside of Xcode, and can be set in Xcode's target configurations for the Xcode itself).

Kornel
  • 97,764
  • 37
  • 219
  • 309