App is damaged and can't be opened, despite being codesigned

I have a toy application that uses CMake to generate a .dmg that contains a simple c++ binary that prints "codesignTest". The binary gets signed by CMake, and I manually sign the .dmg. I am using the "Unix Makefiles" generator, and am signing with a Developer ID Application certificate with a Private Key.

Despite this, I still get an "App is damaged and can't be opened" error when running the binary on a secondary test MacOS machine.

I've created a github repository with instructions on how to reproduce this problem, and I've copy/pasted the binary's signature below. Is there anything invalid with my signature? Thank you.

cisl-ridgeland:~ pearse$ codesign -dv --verbose=4 /Applications/codesignTest.app/Contents/MacOS/codesignTest
Executable=/Applications/codesignTest.app/Contents/MacOS/codesignTest
Identifier=codesignTest
Format=bundle with Mach-O thin (arm64)
CodeDirectory v=20400 size=496 flags=0x0(none) hashes=10+2 location=embedded
VersionPlatform=1
VersionMin=786432
VersionSDK=787200
Hash type=sha256 size=32
CandidateCDHash sha256=df158907d48f1eb3f5ef7b145d43d114bff0c6c3
CandidateCDHashFull sha256=df158907d48f1eb3f5ef7b145d43d114bff0c6c3e2564197c4a69594500f7f66
Hash choices=sha256
CMSDigest=df158907d48f1eb3f5ef7b145d43d114bff0c6c3e2564197c4a69594500f7f66
CMSDigestType=2
Executable Segment base=0
Executable Segment limit=16384
Executable Segment flags=0x1
Page size=4096
Launch Constraints:
None
CDHash=df158907d48f1eb3f5ef7b145d43d114bff0c6c3
Signature size=9045
Authority=Developer ID Application: University Corporation for Atmospheric Research (DQ4ZFL4KLF)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Nov 1, 2023 at 9:43:36 AM
Info.plist=not bound
TeamIdentifier=DQ4ZFL4KLF
Sealed Resources=none
Internal requirements count=1 size=172

Replies

I have a cluster of posts, starting at Resolving Trusted Execution Problems, that explain how to debug issues like this. I’m not 100% certain but it sounds like you’re trying to run a command-line tool by double clicking it in the Finder. If so, you’re likely hitting the issue discussed in the Tool Blocked by Gatekeeper section of Resolving Gatekeeper Problems. It’s easy to check that: In a situation where you’re having the Gatekeeper problem, try running the tool from within Terminal.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for the reply eskimo. The posts that you wrote actually got me started on this effort.

I am indeed running it through the Terminal, not Finder. Wouldn't Gatekeeper block the application regardless of where it's being launched from?

When I check the signature using your suggested method, codesign -v -vvv --strict --deep /Applications/codesignTest.app I get the following output. It's not clear to me what is wrong but I'll keep looking.

cisl-ridgeland:~ pearse$ codesign -v -vvv --strict --deep /Applications/codesignTest.app
/Applications/codesignTest.app/: code has no resources but signature indicates they must be present

I am indeed running it through the Terminal, not Finder. Wouldn't Gatekeeper block the application regardless of where it's being launched from?

Well, it should, but it doesn’t, which is why we consider it a bug (r. 58097824)-:

code has no resources but signature indicates they must be present

Consider this sequence:

% cp "/usr/bin/true" "MyTrue"
% codesign -s - -f "MyTrue"  
MyTrue: replacing existing signature
% codesign -d -v "MyTrue" 
…
Format=Mach-O universal (x86_64 arm64e)
…

I make a copy of the true command and re-sign it. When I dump the signature, codesign shows the Format property as a plain Mach-O.

Now let’s put this in an app wrapper and re-sign:

% mkdir -p "MyTrue.app/Contents/MacOS"
% mv "MyTrue" "MyTrue.app/Contents/MacOS/"
% codesign -s - -f "MyTrue.app"
MyTrue.app: replacing existing signature
% codesign -d -v "MyTrue.app"
…
Format=bundle with Mach-O universal (x86_64 arm64e)
…

Here codesign has detected the app wrapper and signed the code as a bundle. Note how the Format property is bundle with Mach-O.

You get this error when you cross the streams:

% trash *  
% cp "/usr/bin/true" "MyTrue"
% codesign -s - -f "MyTrue"  
MyTrue: replacing existing signature
% mkdir -p "MyTrue.app/Contents/MacOS"
% mv "MyTrue" "MyTrue.app/Contents/MacOS/"
% codesign -v "MyTrue.app"
MyTrue.app: code has no resources but signature indicates they must be present
% codesign -v "MyTrue.app/Contents/MacOS/MyTrue" 
MyTrue.app/Contents/MacOS/MyTrue: code has no resources but signature indicates they must be present

codesign detects that the executable is in an app wrapper and expects it to be signed as a bundle. However, it’s still signed as a plain Mach-O and thus the cryptic error message.


There are two ways to fix this, depending on your goals:

  • If you plan to ship an app that the user can double click in the Finder, construct your app wrapper correctly and then sign that. This is one of the reasons why [Creating Distribution-Signed Code for Mac][refCDSCfM] says “When signing bundled code … use the path to the bundle for PPP, not the path to the bundle’s main code.” It helps to avoid traps like this.

  • If you plan to ship a tool that a user runs from Terminal, put it in a directory structure that doesn’t look like a bundle.

That last point is tricky because there are no documented rules for how codesign detects whether something is a bundle [1]. My general guideline is to put the tool in a directory whose name doesn’t match the tool name. The classic Unix-y bin works for this, but if you don’t want to go down that path you can use something macOS-y, like Command-Line Tools.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Remember Apple’s non-macOS platforms use a ‘shallow’ bundle structure, so codesign might interpret MyTrue/MyTrue as a bundle!