Cariad's Code Blog

macOS application signatures + notarisation (in a nutshell)

by Cariad EcclestonThursday January 2, 2020 • 6 minute read

A little bit of knowledge can go a long way when you’re troubleshooting an old(-er)(-ish) macOS application that won’t open.

Apple would really, really, really prefer you downloaded all your applications from the App Store, where they can assess and block less-than-wholesome code before it gets onto your Mac.

But until – if ever – everything you need is available in the store, Apple employs a handful of techniques to defend you against malicious code on the wild Internet.

Two of these techniques are developer signatures and notarisation.

Developer signatures

If you want to build macOS applications, you’ll likely join the Apple Developer Program.

As well as giving you access to all the support and resources you’ll need, the program also gives you a unique Developer ID and unique keys for signing your builds.

These signatures allow macOS to discover:

  1. Who wrote an application, and:
  2. If the application was modified after the developer released it.

Why does macOS care? Well:

  1. macOS won’t run an application without a signature, and:
  2. macOS won’t run an application that was modified after it was signed.

Who signed an application?

Generally, you don’t need to go and manually check who signed an application before you run it. macOS takes the default position of “signed good, unsigned bad” and refuses to run any application if it can’t identify the developer.

But if you’re paranoid – and that tends to be a good thing in SecOps – then fill your boots!

The spctl command can assess an application to discover who signed it:

usage:
spctl -vv --assess <filename>

For example, I can find out who signed the Firefox build on my machine by running:

terminal:
spctl -vv --assess /Applications/Firefox.app

In the “origin” response, we can see that the Mozilla Corporation signed the build, which shouldn’t be a huge surprise:

response:
/Applications/Firefox.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: Mozilla Corporation (43AQ936H96)

Unsigned applications

By default, macOS won’t run applications that haven’t been signed by their developer.

If you try, macOS tells you that the application “cannot be opened because it is from an unidentified developer.”

s3headersetter is one of my open-source applications, and I don’t sign the builds. If I download and run the latest release, well…

A macOS warning message saying "s3headersetter cannot be opened because it is from an unidentified developer."

Well, let’s be accurate here. macOS won’t allow Finder to run unsigned applications. I got that warning above by double-clicking the application. macOS does allow terminals to run unsigned applications.

Since s3headersetter was built for scripts to run from the command line, I haven’t bothered to include a signing stage during the build. Maybe one day I’ll need to, but why do today what you can put off until tomorrow?

Modified applications

If I was a bit evil, I might want to take someone else’s signed application and modify it to do cheeky things.

What if, for example, I took a signed build of Firefox and patched it to upload the user’s passwords to my secret server. Could I trick my enemies into running it?

The short answer is no.

Since the developer’s signature is a cryptographic hash of the entire application bundle, modifying the bundle would invalidate the signature and macOS wouldn’t run it.

To try that out, I delved into my installed Firefox bundle and made an innocuous change to a property list:

A macOS warning message saying "firefox cannot be opened because the developer cannot be verified."

If we assess Firefox with spctl, we can confirm that there’s a mismatch between the bundle and the signature:

response:
2019-12-28 21:03:21.745 spctl[1048:25051] There was an error parsing the Info.plist for the bundle at URL <0x7f9d26503a70>: NSCocoaErrorDomain - 3840
/Applications/Firefox.app: invalid Info.plist (plist or signature have been modified)

What about compromised developers?

One thing this doesn’t prevent, mind, is a developer actually signing malware.

It doesn’t even need to be intentional.

Just because a developer has signed an application doesn’t mean it contains wholesome code. The next step, then, is notarisation.

Notarisation

A notary is a witness to the authenticity of a document. Notarisation, then – in this context, at least – is the act of Apple being witness to the authenticity of your application.

“Authenticity” in this case means your application bundle contains no more or less than what it claims.

To be a bit more specific: notarisation checks if an application bundle contains any known malware.

Apple hosts an automated notarisation service, to which developers can submit their applications for a scan. If the application is clean, the notarisation service writes another signature – or ticket – to the bundle to verify that Apple has borne witness to its authenticity.

Notarisation is not the same as seeking App Store approval. Notarisation is entirely automated – without manual approval by Apple employees – and doesn’t check against any store or ethical policies. Also, notarisation can only check for known malware.

It’s not foolproof, but it’s far better than nothing.

Checking if Apple has notarised an application

The same spctl command we used earlier can tell us if Apple has notarised an application.

Let’s use it to assess Firefox and Visual Studio Code:

terminal:
spctl -vv --assess /Applications/Firefox.app
spctl -vv --assess "/Applications/Visual Studio Code.app"
response:
/Applications/Firefox.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: Mozilla Corporation (43AQ936H96)

/Applications/Visual Studio Code.app: accepted
source=Developer ID
origin=Developer ID Application: Microsoft Corporation (UBF8T346G9)

Check out the differences in the two “source” responses. Firefox has a “Notarized Developer ID”, while Visual Studio Code has just a “Developer ID”.

Nope, Apple hasn’t notarised this build of Visual Studio Code. It doesn’t contain that additional signature. That’s not to say it failed notarisation. We can’t make that assumption. All we know is, Apple hasn’t notarised it.

But… can we run it anyway?

Running an unnotarised application

If you try to run an unnotarised application, you’ll get a warning message and no option to carry on regardless:

macOS dialog showing “Visual Studio Code can’t be opened because Apple cannot check it for malicious software."

Since notarisation is a relatively new concept, Apple does let you bypass this check if you really, truly trust the developer and are willing to jump through a hoop to prove you accept responsibility for the risk:

  1. Context-click on the application.
  2. Click Open.
  3. In the warning pop-up, click Open again.

Ta-da!

demonstration:

macOS remembers when you bypass a check for an application, so you won’t need to go through those steps again – for that application. Running a different unnotarised application would trigger a new warning.

TL;DR - what good do these do?

In a nutshell:

  1. Developer signatures verify that the developer you trust genuinely built the application you want to run.
  2. Developer signatures verify that an application wasn’t modified after the developer signed it.
  3. Notarisation verifies that Apple is a witness to an application being clear of known malware.