Recently (as of 2023-11-01) Apple has changed their notarization process.
I took the opportunity to drop Apple's own tools for this process (notarytool
) and switch to a Python-based solution using their documented Web API for notarization
This works great and has the additional bonus, that I can now notarize macOS apps from linux (in the context of CI, I can provision linux runners much faster than macOS runners). hooray.
Since this went so smooth, I thought about moving more parts of my codesigning process to linux, and the obvious next step is find a solution for stapling the notarization tickets into application, replacing xcrun stapler staple MyApp.app
With the help of -vv
and some scraps of online documentation, it turns out that it is very simple to obtain the notarization ticket if you know the code directory hash (CDhash
) of your application.
the following will return a JSON-object containing (among other things) the base64-encoded notarization ticket, which just has to be decoded and copied into the .app bundle for stapling:
cdhash=8d817db79d5c07d0deb7daf4908405f6a37c34b4
curl -X POST -H "Content-Type: application/json" \
--data "{ \"records\": { \"recordName\": \"2/2/${cdhash}\" }}" \
https://api.apple-cloudkit.com/database/1/com.apple.gk.ticket-delivery/production/public/records/lookup \
| jq -r ".records[0] | .fields | .signedTicket | .value"
So, the only thing that is still missing for my stapler
replacement is a way to obtain the code directory hash for a given application.
On macOS (with the XCode tools installed), I can get this hash with codesign -d -vvv MyApp.app
, but this obviously only works if I have the codesign
binary at hand.
I've found a couple of python wrappers for stapling tickets, but all of them just call xcrun stapler staple
under the hood.
This is not what I want.
So my question is:
How can I extract the code directory hash (CDhash
) from a macOS application, without using macOS specific tools? (That is: How are CDhash
es generated? I haven't found any documentation on this)
I would very much like to use use Python for this task. Ideally, such a solution would be cross-platform (so I can use it on macOS and Linux, and probably others as well).
How can I extract the code directory hash (CDhash) from a macOS application, without using macOS specific tools?
The CDhash of an app is the CDhash of the main executable in Contents/MacOS as identified in Contents/Info.plist
Each hash is stored at the end of the binary segment for each architecture in an XML statement. It can be grepped out.
The embedded cdhash is encoded in base64. The first one is for intel, the second for apple silicon:
% grep -i -a -A3 'cdhashes' myApp.app/Contents/MacOS/mainexec | sed -n '4p;9p' | cut -f 3
HPhKLQv1j2SFYTmIgyUi/L6B9Yo=
TVNDrCQEL9A/DMWVmphntZAq7kc=
% printf "HPhKLQv1j2SFYTmIgyUi/L6B9Yo=" | base64 -d | hexdump -v -e '/1 "%02x" ' && echo ""
1cf84a2d0bf58f6485613988832522fcbe81f58a
% printf "TVNDrCQEL9A/DMWVmphntZAq7kc=" | base64 -d | hexdump -v -e '/1 "%02x" ' && echo ""
4d5343ac24042fd03f0cc5959a9867b5902aee47
Compared with the cdhash as reported by codesign:
% codesign -dvvv -a arm64 myApp.app
Executable=myApp.app/Contents/MacOS/mainexec
Identifier=com.mycompany.myApp
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=92199 flags=0x10000(runtime) hashes=2870+7 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha256=4d5343ac24042fd03f0cc5959a9867b5902aee47
CandidateCDHashFull sha256=4d5343ac24042fd03f0cc5959a9867b5902aee4725c5a75775cd711aae76b709
Hash choices=sha256
CMSDigest=4d5343ac24042fd03f0cc5959a9867b5902aee4725c5a75775cd711aae76b709
CMSDigestType=2
Launch Constraints:
None
CDHash=4d5343ac24042fd03f0cc5959a9867b5902aee47