How to sign and notarize an Electron app for macOS

May 15, 2024

So you made a macOS app, shared it with your friends, and they encountered one of those dreaded popups:

App cannot be opened because it is from an unidentified developer App can’t be opened because Apple cannot check it for malicious software

Then you need to sign (left) and notarize (right) your app!

Note: in the meantime, the app can still be opened by right clicking on it and clicking Open from the context menu.

Signing consists in buying a developer membership with Apple, which will let you create a key that you can use to codesign your app with.

Notarizing consists in uploading your app to an Apple service that scans it for malware. If it passes the process, your app gets a “stamp of approval” that is bundled with your app and also mirrored on Apple’s Gatekeeper servers.

In the case of Electron, here’s some relevant docs this article is based on:

Get a signing certificate

The first step is to generate a signing keypair, and get a signing certificate from Apple, which requires you to subscribe to Apple’s developer program.

Then, follow create a certificate signing request.

In my experience, it doesn’t seem that the User Email Address you input matters.

As for Common Name, it seems to only affect how the private and public key are named in Keychain Access.

By saving the request to disk, you will get a CertificateSigningRequest.certSigningRequest file.

This will also create a Common Name.p12 and Common Name.pem entry in your Keychain Access. The .p12 is the private key, and the .pem is the public key, that are going to be associated with the certificate you’re requesting.

You should now upload the .certSigningRequest file to your Apple Developer account, in Certificates, IDs & Profiles. Choose the Developer ID Application certificate type.

This will give you a certificate developerID_application.cer that you need to import in Keychain Access (by simply opening it).

Note: if it refuses to open with “The System Roots keychain cannot be modified”, it’s because Keychain Access opens by default in the System Roots keychain and you can only update it as superuser (so mainly from the CLI with sudo security ...).

We only need this certificate in the login keychain, so make sure it’s the selected one in Keychain Access and then open the certificate again.

Get the intermediate certificate

In Apple’s PKI, your developer certificate is signed by an intermediate certificate, that’s itself signed by one of Apple’s root certificates.

The Apple root certificates should already be present out of the box in your System Roots keychain, but the intermediate ones are not, and need to be downloaded for the certificate chain to be complete and for codesign to work.

When you created your Developer ID certificate, you were likely prompted between “G2 Sub-CA” and “Previous Sub-CA”. At the time of writing, on the Apple PKI page, those are respectively Developer ID - G2 (Expiring 09/17/2031 00:00:00 UTC) and Developer ID - G1 (Expiring 02/01/2027 22:12:15 UTC). So go ahead and download the appropriate one and install it to your login keychain just like your developer certificate.

Note: if you don’t know what intermediate certificate you need, you can see what CA signed your certificate like so:

$ openssl x509 -in "developerID_application.cer" -noout -issuer
issuer=CN=Developer ID Certification Authority, OU=G2, O=Apple Inc., C=US

In this case, you can see my certificate was issued using the G2 intermediate certificate.

Warning: leave the certificate trust settings to “Use System Defaults” and do not mark it as “Always Trust”, both for your developer certificate and the intermediate certificate.

If like me you were getting issues signing things and assumed marking as “Always Trust” could help, beware, it does the opposite and prevents codesign from working altogether for some reason, even after you fix the actual cause of your signing issues. 😅

Whether you’re missing a certificate, or if you have the right certificates but they’re marked as “Always Trust”, you’ll get that same error:

Warning: unable to build chain to self-signed root for signer

Manually sign your app

You’re now in a place where you can manually sign your app:

codesign --sign 'Developer ID Application: MyApp (ID)' MyApp.app

To find the identify to pass to --sign:

security find-identity -v -p codesigning

-v will show only valid identities. -p is for selecting a specific policy, here we care about codesigning.

Signing your app with Electron Forge

Add osxSign to your forge.config.js:

module.exports = {
  packagerConfig: {
    osxSign: {
      identity: 'Developer ID Application: MyApp (ID)'
    }
  }
}

If you only have one valid code signing identity configured on your Mac, you can omit the identity parameter. You still need to pass an empty object osxSign: {}.

Notarizing your app with Electron Forge

Add osxNotarize to your forge.config.js. There’s a few ways to configure it documented here.

The documentation is pretty clear and complete so I won’t bother repeating anything here. 🙂

Debugging osxSign and osxNotarize

If you encounter issues where Electron is not properly signing or notarizing your app, you can debug the signing and notarizing process that way:

DEBUG=electron-osx-sign,electron-notarize* npx electron-forge package

This will output detailed logs that should help you identify the culprit.

Conclusion

That should be all you need to have your app approved by Apple so that you can share it with the world. 🌎

Happy building! 🚀

Want to leave a comment?

Join the discussion on X or send me an email! 💌
This post helped you? Buy me a coffee! 🍻