One way to run apps in private, without them having periodic online checks with Apple’s OCSP service, is to strip their signatures, which you can also do as a last resort if there’s anything wrong with a signature. This shouldn’t be necessary if the signing certificate has simply expired, though, as the test applied by macOS is whether the certificate was valid at the time the app was signed. This article looks at what you lose if you strip an app’s signature, or put the other way around, what code signatures do.
The three main purposes of signing an app are:
- to establish the identity of the developer, together with a chain of trust from that going back to Apple as Certificate Authority (CA);
- to provide a cdhash for verification of the integrity of all its executable code;
- to establish app entitlements, which are essential when the app runs in a sandbox, and increasingly important for many other apps too.
To look properly at code signatures in all their glory, you can use my free app Taccy, or the command
codesign -dvvv --entitlements - Appname.app
for the app named Appname.app. There are several other excellent apps available.
Apps are signed using a security certificate valid for the purpose. Depending on how you’ve obtained the app, the authority given for that security certificate may be the developer or Apple.
Apps distributed through the App Store normally come with an Apple Mac OS Application Signing certificate, with a TeamIdentifier which identifies the developer; in contrast, apps which are distributed by the developer are signed with that developer’s Developer ID Application certificate, which names the developer. The intermediate certificates also differ, with Apple Worldwide Developer Relations Certification Authority for App Store apps, and Developer ID Certification Authority for independently distributed apps. The root certificate is common to both, Apple Root CA. Thus, a developer-signed app might have the following certificates:
Authority=Developer ID Application: Howard Oakley (QWY4LRW926)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=17 Aug 2020 at 16:59:17
and the secure timestamp at the end verifying the time of signing.
As the CA, Apple does revoke certificates, notably those which have been abused to sign malware. Revocation checks are now performed online using OCSP.
Ad hoc certificates can’t establish identity, nor can they form part of a chain of trust. However, they do ensure that a cdhash is created to cover executable code.
The most visible evidence of the signature is the folder named _CodeSignature within the app bundle, which contains the Code Directory Hash or cdhash, covering those parts of the bundle containing executable code. This is used in full signature checks to determine whether executable parts of the app have changed since the app was signed by the developer. Being a cryptographic hash, it’s sensitive even to single bit changes. If you want full details of how the cdhash works, this article is invaluable.
Full signature checks are relatively unusual, and can be triggered when an app is run from a previously unknown path, or macOS suspects there may have been a change made to the app, for example. Far more common is the use of the cdhash as a fingerprint: every time an app or other signed code is run, macOS checks whether its cdhash is known from the security database. It’s this use which sets the requirement that all ARM executable code has been signed, to save having to compute the cdhash and check it during every app launch.
When you inspect an app’s signature, you’ll see the cdhash reported in the following format:
Hash type=sha256 size=32
The great majority of App Store apps are required to run in their own sandbox, which essentially prevents them from doing anything outside narrow limits. As most of them need to access other files and macOS features which aren’t available inside the sandbox, there’s a system by which the app can request exceptions to be made, known as entitlements. Together with the sandbox setting, these are baked into the app’s signature.
A typical set of entitlements reported from an app signature might be:
com.apple.security.files.bookmarks.app-scope : 1
com.apple.security.files.user-selected.read-write : 1
com.apple.security.files.bookmarks.document-scope : 1
com.apple.security.print : 1
com.apple.security.app-sandbox : 1
com.apple.security.network.client : 1
Many entitlements have fairly obvious meaning, and Apple provides a list of those it wants developers to use.
Apple’s original reference to entitlement keys was last updated five years ago, in March 2017, and is now woefully out of date, and doesn’t of course cover those for hardened apps. More recent lists are provided by Apple for sandboxed apps, and for those which are hardened and notarized. If you can’t find an entitlement in any of those lists, try looking in Jonathan Levin’s database of all known entitlements for iOS and macOS, which is as comprehensive as we’re likely to get.
The tell-tale entitlement of
com.apple.security.app-sandbox puts that app into a sandbox. Notarized apps normally require a hardened runtime, which isn’t set by an entitlement, but by a special ‘runtime’ flag given in the CodeDirectory:
CodeDirectory v=20500 size=1155 flags=0x10000(runtime) hashes=27+5 location=embedded
Notarization itself isn’t part of the code signature, and may be ‘stapled’ as a ‘ticket’. In Taccy it’s reported in its ‘source’:
source=Notarized Developer ID
If you don’t use Taccy or a similar app which reports notarization status, you’ll need to use
spctl to see this, as
codesign doesn’t reveal it.
Strip a signature and you also remove both its sandbox entitlement and all others. For many apps, including those that don’t run in a sandbox, this can make a big difference: apps require special entitlements for features like push notifications, iCloud access, and more. Before stripping an app, it’s well worth checking whether that will have significant consequences, by inspecting its entitlements first.
Universal apps & command tools
Although most apps containing both Intel and ARM code, so they can run natively on both Mac architectures, are signed as one, executable code for individual architectures is actually signed separately. It’s therefore possible to sign only the ARM code, or to strip a signature from just the Intel code. This is complex, and rarely done.
Traditionally, before notarization and Apple Silicon Macs, many command tools consisted of a single Mach-O binary which wasn’t signed. It has, though, been possible to sign command tools, which results in the signatures being added to the executable file, and that’s now required for those which run native on ARM processors, are notarized, or are embedded in App Store apps.
Revised with corrections 16 March 2022.