Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
How to notarize a Unity build for MacOs 10.15 Catalina

How to notarize a Unity build for macOs 10.15 Catalina

As of January 2020, all apps running on macOs 10.15 Catalina are required to be notarized. For Unity games distributed outside the Mac App Store, such as with Steam, the notarization process is done post build using a series of Xcode command line tools.

Prerequisites

  • a Mac that is compatible with macOs 10.15 Catalina :
    • MacBook (2015 or newer)
    • MacBook Air (2012 or newer)
    • MacBook Pro (2012 or newer)
    • Mac mini (2012 or newer)
    • iMac (2012 or newer)
    • iMac Pro (from 2017)
    • Mac Pro (2013 or newer)
  • macOs 10.15 Catalina installed
  • Xcode 11.0 installed
  • Apple developer account at https://developer.apple.com/
  • Apple Id account at https://appleid.apple.com

Developer ID Application certificate

This certificate will be used for code signing the build. If you don't already have one, you can create one in the account section of the Apple developer website https://developer.apple.com/account/resources/certificates/add

In the "Create a New Certificate" section, select to add a "Developer ID Application" certificate. After clicking continue, you should see further instructions about how you'll first need to create and upload a "Certificate Signing Request" using the Keychain Access app https://help.apple.com/developer-account/#/devbfa00fef7

After you've uploaded the Certificate Signing Request file, you should then be able to download the Developer Id Application certificate. Once downloaded, clicking on the file should add it to Keychain Access where you'll see it under the certificates section. It will be called something like "Developer ID Applicate : YourCompanyName (0123456789)"

Generated Password

To upload a build to Apple servers you'll need to use a "generated password". To create one, go to https://appleid.apple.com and then in the "Security" section click on "Generated Password..."

The password you generate will look similar to the format abcd-efgh-ijkl-mnop

Unity Build & Player Settings

  • In the Build Settings, target platform should be set to Mac OS
  • In Player Settings, use default settings and set a unique Bundle Identifier

Entitlements file

This is an xml file used to give executable permissions to the app when code signing. In particular, all apps need to have "Hardened Runtime" entitlements. Here are the minimum entitlements needed for a Unity build :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
	       <key>com.apple.security.cs.disable-library-validation</key>
	       <true/>
	       <key>com.apple.security.cs.disable-executable-page-protection</key>
	       <true/>
    </dict>
</plist>

Save this file as "YourGame.entitlements". Additional entitlements can be found at https://developer.apple.com/documentation/bundleresources/entitlements

Notarization

The following steps use the Terminal command line and assume your build and entitlements file are in the same directory.

Change all file permissions in the app

For the code signing to work in a later step, we need to change permissions for files within the app directory.

chmod -R a+xr "YourGame.app"

Code sign the app

Next, in the command line, we need to use the codesign tool on the permission changed files by using your Developer ID Application certificate (literally the name of the certificate in double quotes).

codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements "YourGame.entitlements" --sign "Developer ID Application : YourCompanyName (0123456789)" "YourGame.app"

If successful, you should see a message similar to:

YourGame.app: signed app bundle with Mach-O thin (x86_64) [com.YourCompany.YourGame]

Create a zip

Once the code is signed, we need to compress the application into a zip file for uploading. You can do this in the command line.

ditto -c -k --sequesterRsrc --keepParent "YourGame.app" "YourGame.zip"

Upload the zip to Apple's notarization service

Now that we have the compressed zip file, we'll want to upload it to the Apple servers for notarization using the xcrun altool in the command line. In order to do this, you'll need your Apple ID username (usually an email address), your Apple ID generated password (the one with the format abcd-efgh-ijkl-mnop) and your Apple Developer "Provider Short Name". Often the "Provider Short Name" is your Team ID (ten digit alphanumeric id), you can find in the membership section of your Apple developer account https://developer.apple.com/account/#/membership/

However, if your "Provider Short Name" is not the same as your Team ID, you can find it by running the following command:

xcrun iTMSTransporter -m provider -u YourAppleIDUsername -p abcd-efgh-ijkl-mnop

Also, you'll need your game's bundle id that you defined in Unity Player Settings. Usually the format for that is similar to com.YourCompany.YourGame

To upload the build to the notarization service, run the command:

xcrun altool --notarize-app --username YourAppleIDUsername --password abcd-efgh-ijkl-mnop --asc-provider ProviderShortName --primary-bundle-id com.YourCompany.YourGame --file YourGame.zip

If the upload was successful, you should see a message with a RequestUUID similar to:

No errors uploading 'YourGame.zip'.
RequestUUID = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

From there, you will need to wait for the notarization service to process the upload. This can take anywhere from 1 minute to an hour or sometimes longer if the service is overloaded. When it's completed you'll get an email with the subject "Your Mac software was successfully notarized". Alternatively, you can ping the service for the current status of the upload using that RequestUUID by running the following command.

xcrun altool --notarization-info xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --username YourAppleIDUsername --password abcd-efgh-ijkl-mnop --asc-provider YourAppleDeveloperTeamID

Staple the app

After notarization is completed, Apple creates a ticket that you need to "staple" to the app. To do that, we'll use the xcrun stapler tool.

xcrun stapler staple "YourGame.app"

If successful you should see the following message:

The staple and validate action worked!

Check notarization

After everything is completed we can use the spctl tool to check if the app is recognized as having the proper notarization.

spctl -a -v YourGame.app

If successful, you should see a message similar to:

YourGame.app: accepted
source=Notarized Developer ID

Now, whether or not the notarization was successful, if you try and open the app on your local machine, everything will appear to work fine. A good way to double check everything is actually working is to upload the notarized build to somewhere on the web (eg Google Drive), download it, and then see if the app opens properly. If working correctly, then all you should see is a small warning that you downloaded it from the web and then it should open normally.

@EddieCameron
Copy link

EddieCameron commented Jun 12, 2020

Using old versions of Unity causes the same issue. For Unity you can hexedit the mono libraries to change their min sdk to 10.9. It should still work for FMOD https://asmaloney.com/2020/03/howto/notarizing-older-unity-games-on-macos/

@dpid
Copy link
Author

dpid commented Jun 14, 2020

@EddieCameron @ThunderboxEntertainment , thanks for the extra tips.

@m3m3x
Copy link

m3m3x commented Jun 14, 2020

Updating Unity and getting FMOD from the asset store both helped, but now I've got one remaining error on a binary in the contents folder that of the same name as my game. It's listed as:

TheLastSurvey_OSXv4.1.app/Contents/MacOS/TheLastSurvey"
"The signature of the binary is invalid."

I tried to codesign just that binary with:
codesign -vvv --deep --strict \path\

As per Apple's recommendations to common issues, but still am getting the error after uploading to notarization using xcrun. Any thoughts?

@m3m3x
Copy link

m3m3x commented Jun 14, 2020

I used @ThuderboxEntertainment's step for codesigning the binary directly with specific reference to my keychain and that seemed to solve the issue. My game is now notarized, stabled, and ready for distro :)

Thanks @dpid & @EddieCameron

@russellquinn
Copy link

russellquinn commented Jun 23, 2020

Everything works for me. And the notarized app works fine on the computer that did the notarizing.

But, when I try to run it on another Mac, it crashes immediately with: Termination Reason: Namespace CODESIGNING, Code 0x1

Any ideas? Thanks.

@dpid
Copy link
Author

dpid commented Jun 23, 2020

@russellquinn , since the termination reason mentions codesigning, I recommend trying out the extra codesigning tips mentioned here in the comments. If one of them works for you, please share what resolved your issue.

@russellquinn
Copy link

russellquinn commented Jun 24, 2020

@dpid I just figured it out! I was stuck on this for two days. I found my answer here: https://developer.apple.com/library/archive/qa/qa1884/_index.html#//apple_ref/doc/uid/DTS40015141

It mentions that you can't run an app signed with "Mac App Store distribution identity, with the certificate’s Subject Common Name starting with “3rd Party Mac Developer Application," which was not my case. I double-checked and was definitely using "Developer ID Application." Curious…

But further in the article it says "Recently, the com.apple.developer.team-identifier entitlement was added to all new Mac provisioning profiles" and this tipped me off.

I had com.apple.application-identifier and com.apple.developer.team-identifier in my .entitlements file — a hangover from when I based my Steam-build .entitlements on my Mac App Store .entitlements. I removed those two entries, redid the entire process and it works!

tl;dr: If you have com.apple.application-identifier and/or com.apple.developer.team-identifier in your .entitlements file the app will still run on the machine you codesigned it on, but it appears to trigger Apple's "distribution builds" check when run on another Mac.

@JordanSchuetz
Copy link

JordanSchuetz commented Jun 25, 2020

Ok everyone, I encountered a pretty huge issue once I went to notarize my application and submit it to Apple. After submission, I got a notification letting me know that my application failed notarization. So to check the error, I ran the command: spctl -a -v MyApp.app but I got the return message in my terminal:

MyApp.app: rejected
source=Unnotarized Developer ID

I had no clue why this was happening because I was able to successfully notarize my previous build. Then I ran the command:

xcrun altool --notarization-info xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --username YourAppleIDUsername --password abcd-efgh-ijkl-mnop --asc-provider YourAppleDeveloperTeamID

And I got a JSON document that was returned.

{
  "logFormatVersion": 1,
  "jobId": "XXXXXXXXXXX",
  "status": "Invalid",
  "statusSummary": "Archive contains critical validation errors",
  "statusCode": 4000,
  "archiveFilename": "MyApp.zip",
  "uploadDate": "2020-06-25T06:02:20Z",
  "sha256": "XXXXXXXX",
  "ticketContents": null,
  "issues": [
    {
      "severity": "error",
      "code": null,
      "path": "MyApp.zip/MyApp.app/Contents/Plugins/discord_game_sdk.bundle",
      "message": "The binary is not signed.",
      "docUrl": null,
      "architecture": "x86_64"
    },
    {
      "severity": "error",
      "code": null,
      "path": "MyApp.zip/MyApp.app/Contents/Plugins/discord_game_sdk.bundle",
      "message": "The signature does not include a secure timestamp.",
      "docUrl": null,
      "architecture": "x86_64"
    },
    {
      "severity": "error",
      "code": null,
      "path": "MyApp.zip/MyApp.app/Contents/Plugins/discord_game_sdk.dylib",
      "message": "The binary is not signed.",
      "docUrl": null,
      "architecture": "x86_64"
    },
    {
      "severity": "error",
      "code": null,
      "path": "MyApp.zip/MyApp.app/Contents/Plugins/discord_game_sdk.dylib",
      "message": "The signature does not include a secure timestamp.",
      "docUrl": null,
      "architecture": "x86_64"
    },
    {
      "severity": "error",
      "code": null,
      "path": "MyApp.zip/MyApp.app/Contents/Plugins/steam_api.bundle/Contents/MacOS/libsteam_api.dylib",
      "message": "The signature of the binary is invalid.",
      "docUrl": null,
      "architecture": "i386"
    },
    {
      "severity": "error",
      "code": null,
      "path": "MyApp.zip/MyApp.app/Contents/Plugins/steam_api.bundle/Contents/MacOS/libsteam_api.dylib",
      "message": "The signature algorithm used is too weak.",
      "docUrl": null,
      "architecture": "i386"
    },
    {
      "severity": "error",
      "code": null,
      "path": "MyApp.zip/MyApp.app/Contents/Plugins/steam_api.bundle/Contents/MacOS/libsteam_api.dylib",
      "message": "The signature of the binary is invalid.",
      "docUrl": null,
      "architecture": "x86_64"
    },
    {
      "severity": "error",
      "code": null,
      "path": "MyApp.zip/MyApp.app/Contents/Plugins/steam_api.bundle/Contents/MacOS/libsteam_api.dylib",
      "message": "The signature algorithm used is too weak.",
      "docUrl": null,
      "architecture": "x86_64"
    }
  ]
}

What I found out was that notarization requires all libraries to be signed, including 3rd party ones. You are required to do a deep signing and force sign libraries you did not build. Okay so you may be wondering how to do this...simply type the follow code below and fill in your own information. This is how you notarize the Steam API:

codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements "MyApp.entitlements" --sign "Developer ID Application : YourCompanyName" "MyApp.app/Contents/Plugins/steam_api.bundle/Contents/MacOS/libsteam_api.dylib""

Then run the same command to notarize the Discord API Steam Bundle:

codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements "MyApp.entitlements" --sign "Developer ID Application: YourCompanyName" "MyApp.app/Contents/Plugins/discord_game_sdk.dylib"

Also you will need to codesign the Discord API bundle as well:

codesign --deep --force --verify --verbose --timestamp --options runtime --entitlements "MyApp.entitlements" --sign "Developer ID Application: YourCompanyName" "MyApp.app/Contents/Plugins/discord_game_sdk.bundle"

If you get this status message, you know it was successful:

signed Mach-O thin (x86_64) [discord_game_sdk]

So now after you have signed each component, now go ahead and continue with the tutorial by signing the entire package.

Hope this helped everyone! Cheers.

Regards,
Jordan Schuetz
https://jordanschuetz.com

@MonkeyGland
Copy link

MonkeyGland commented Jul 2, 2020

Hi

Many thanks for this guide - very much appreciated.

I've followed the steps successfully with apple notarizing the app etc.

Issues comes when I test the app post stapling. I check the notarization using spctl -a -v YourGame.app and all is well.

However, when I upload to GDrive and then download to test, I get the dreaded "The application cannot be opened". I check that version with spctl and get "a sealed resource is missing or invalid".

I followed @ThuderboxEntertainment steps too.

Anyone got any ideas?

Thanks in advance all

@GiovanniFrigo
Copy link

GiovanniFrigo commented Jul 2, 2020

@MonkeyGland if you uploaded the .app file to google drive directly, bad things could happen. Never upload a .app file to google drive without zipping it first!

MacOS apps are basically just directories, and google drive treats them like so, so it can change properties of internal app files.

@MonkeyGland
Copy link

MonkeyGland commented Jul 2, 2020

@GiovanniFrigo D'oh - I can't believe I missed that. Thank you mate - much appreciated.

@JaroslavHolan
Copy link

JaroslavHolan commented Jul 15, 2020

Hi @dpid
How to fix unsealed contents present in the bundle root?

We have the same problem. Many comments advise us to delete .meta files. But we do not have any .meta file in our bundle .app.
Can anyone advise?

@EddieCameron
Copy link

EddieCameron commented Jul 18, 2020

I made a shell script based on this guide to automate some of the process https://github.com/EddieCameron/notarize-app/

@sweatyc
Copy link

sweatyc commented Sep 29, 2020

Same here, zip -r didn't work and gave “The signature of the binary is invalid.” errors.

Using ditto -c -k --sequesterRsrc --keepParent <App>.app <App>.zip fixed the problem! @dpid please fix this.

@dpid
Copy link
Author

dpid commented Sep 29, 2020

Updated the gist to show the ditto command. Thank you for the tip, @sweatyc @hallgrimgames

@PavelMo4alov
Copy link

PavelMo4alov commented Oct 30, 2020

Very good article! I signed a lot of builds )))) But today I noticed that my app not request microphone permission after staple =( What I need to add in entitlements file? Help me please! Thank you!

I find answer:
com.apple.security.device.audio-input

@AlexanderHJohnstone
Copy link

AlexanderHJohnstone commented Jan 24, 2021

Thanks for the awesome guide. I'm now getting the following message on the notarization command:

altool[18217:1491434] CFURLRequestSetHTTPCookieStorageAcceptPolicy_block_invoke: no longer implemented and should not be called

It seems to still upload and notarize ok, but wondering if this should be fixed or if it's just a quirk on my end.

@widVE
Copy link

widVE commented Feb 3, 2021

Thank you so much for this guide. I've worked through all of the steps with success, however once I upload a zip of the final stapled, successfully notarized application, and then re-download it, unzip, and try to run, I get a plain "The file can't be found" error message. Using MacOS Catalina 15.7, Unity 2020.2.1f1. Note - I only codesigned the main app file, nothing within (but didn't receive any errors) - any chance this may be the culprit?

@Jiaquarium
Copy link

Jiaquarium commented Feb 17, 2021

Thank you so much for this. I made a simple Makefile script based on this to automate the process here.

Just replace all the variables in the Makefile with your own (or use env variables) and assumes your build folder is set up like in the repo. Hope this saves some people some time.

@dean-ivre
Copy link

dean-ivre commented Feb 19, 2021

Im getting this error unsealed contents present in the bundle root @dpid @JaroslavHolan

@jasonzhetan
Copy link

jasonzhetan commented Mar 15, 2021

Thank you for the amazing guide! I signed and notarized the app with no problem. However, when I try to tuck a signed app into a .pkg, then try to notarize it, it returns as failed with "The signature of the binary is invalid" for the executable in Contents/MacOS. Some Googling has indicated either a problem with nesting or extended attributes. Does anyone here have any experience or ideas with solving this issue?

@aDu
Copy link

aDu commented Apr 10, 2021

Thanks for this, you are doing God's work. <3

Extremely concise and to the point, was very quick to execute and understand the instructions. 5/5. I owe you a beer mate.

@drew-512
Copy link

drew-512 commented Apr 14, 2021

THANK YOU

@Bakelord
Copy link

Bakelord commented Apr 29, 2021

Massive thanks for what is arguably the greatest achievement on the internet - a thorough guide the beat the nonsense of Apple's Notorization.

@wysiwyggins
Copy link

wysiwyggins commented May 19, 2021

Everything goes as described, but the resulting app gives a "You do not have permission to open the application “(app name)”, Contact your computer or network administrator for assistance". error when you launch it

@StefanOber
Copy link

StefanOber commented May 31, 2021

Thank you so much! Very detailed and understandable. You saved me a lot of time.

@MashupGamingDKK
Copy link

MashupGamingDKK commented Jun 17, 2021

Everything goes as described, but the resulting app gives a "You do not have permission to open the application “(app name)”, Contact your computer or network administrator for assistance". error when you launch it

Same thing happened to me, I found out it is something caused by codesign with a bad entitlements, I am still not sure what is the correct entitlements but I found this little asset made by someone else.

https://github.com/cunum/unity-osx-notarize

edited: the password refers to the generated password, not your apple id password, should be something like aaaa-bbbb-cccc-dddd
It worked for me.

Some other related material should be on this https://forum.unity.com/threads/notarizing-osx-builds.588904/

The writer for this article also replied something on the above forum.

Hope it can help you even after so long time.

@Can0nC
Copy link

Can0nC commented Oct 25, 2021

Hi everyone, I was able to go through the process and successfully notarize the app. But unfortunately, for some reason, it broke the app, which lead to the error DllNotFoundException: Unable to load DLL 'agoraSdkCWrapper': The specified module could not be found. (The app was able to run without any error before notarization).

Any help or suggestions? Thank you!

@Cjericho4
Copy link

Cjericho4 commented Nov 28, 2021

I made a Makefile based on other comments made here that uses the codesign command given by @ThunderboxEntertainment as that was the only one that would work for the app that we made, I also found that if you only run the first command that he gives you can notarize without signing the application itself. You can find the Makefile here: https://github.com/Cjericho4/UnityMacBuildSigner

@cuikeqiang
Copy link

cuikeqiang commented Nov 21, 2022

Try sign every bundle in your "xxx.app".
Something like:

ITEMS=""
PLUGIN_DIR="${app_file}/Contents"
if [ -d "$PLUGIN_DIR" ] ; then
PLUGIN=$(find "${PLUGIN_DIR}" -depth -type d -name ".framework" -or -name ".dylib" -or -name "*.bundle" | sed -e "s/(.*framework)/\1/Versions/A//")
RESULT=$?
if [[ $RESULT != 0 ]] ; then
exit 1
fi

ITEMS="${PLUGIN}"

fi

ITEMS="${ITEMS}"$'\n'"${app_file}"

echo "Found:"
echo "${ITEMS}"

for ITEM in $ITEMS;
do
if [[ ${ITEM} == BUNDLE_YOU_WANT_TO_SKIP ]] ; then
echo "Skip '${ITEM}'"
continue
fi

echo "Signing '${ITEM}'"

/usr/bin/codesign --force -s "${sig}" --options runtime "${ITEM}" -v --entitlements "YOUR_ENTITLEMENTS_FILE.entitlements"
RESULT=$?
if [[ $RESULT != 0 ]] ; then
    echo "Failed to sign '${ITEM}'."
    exit 1
fi

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment