Press "Enter" to skip to content

.NET Core Application macOS Packaging and Notarization

When building a .NET Core application that will run on Mac you might want to distribute it outside from Mac App Store. In this article, I will provide a step by step guide on how to package and notarize a .NET Core application to achieve distribution.

Let’s Start!

For this guide, I created a simple .NET Core Console application that outputs “Hello World”. Now let’s go step by step:

1. Publish app as self-contained

We use the following command in Terminal to publish for macOS:

dotnet publish -r osx-x64

By publishing the app as self-contained it produces a macOS 64-bit executable.

If you created the application on Windows, make sure to run chmod +x AppName (Path to unix executable) from a Unix machine. Otherwise, the app will not start on macOS. To create an app bundle there are several ways to achieve this:

You can find more information about this here.

2. Create an app bundle

To create an app bundle there are several ways to achieve this you can follow this awesome guide or use a tool that generates it. For this sample, I’m using Platypus which provides a graphical user interface for creating a Mac Application bundle based on a shell script.

1) In the Platypus Script Path

Create a new script with the following:

     #!/bin/bash
    ./APP_NAME

APP_NAME is the name of your application Unix executable file

In the specific case of this sample the script content is:

#!/bin/bash
./MacNotarizationSample

2) Fill the following information:
App Name: Name of the application
Icon: Application icon
Identifier: Bundle identifier of the application
Author: Author of application
Version: The version number of application build

3) Add all files that were generated when the app published as self-contained (including the Unix executable) to Platypus bundled files.

4) Click on Create App

Voila!

But there’s a problem if we open the generated app bundle in other Mac we will get the following result:

To avoid this we need to notarize the application that brings us to our next step.

3. Create a Developer ID Application

To be able to distribute outside of the Mac App Store we need to use a Developer ID Application certificate.

4. Use the notarization script

I created a bash script called app_notarizer.sh that allows you to do the following:

  • Create a notarization request
  • Check notarization request status
  • Staple the notarization to your app.
sh app_notarizer.sh [-n|s|c] [ -a APP_NAME ] [ -i SIGNING_IDENTITY ] [ -e ENTITLEMENTS ]  [ -b BUNDLE_ID ] [ -u USERNAME ] [ -p PASSWORD ] [ -v PROVIDER ] [ -k UUID ]

Script Requirements:

By using this script you can notarize the application by following these steps:

1) Create a notarization request
2) Check notarization request status
3) Once notarization status comes as successful, staple notarization

For information on the script:

 sh app_notarizer.sh --help

Create a notarization request:

Syntax:
[ -n | --notarize ] [ -a | --file APP_NAME ] [ -i SIGNING_IDENTITY ] [ -e ENTITLEMENTS ]  [ -b BUNDLE_ID ] [ -u USERNAME ] [ -p PASSWORD ] [ -v PROVIDER ]

Parameters:
[ -a | --file  ]  - File name
[ -s ]            - Apple Signing identity
[ -e ]            - Application entitlements file
[ -b ]            - Application Bundle identifier
[ -u ]            - Apple Developer ID Username
[ -p ]            - Application Specific password
[ -v ]            - Access Provider

Example:
.app   sh app_notarizer.sh --notarize -a MyApp.app  -b com.company.myapp  -u myappleaccount@gmail.com -p aaaa-aaaa-aaaa-aaa -v FFFFFFFF -e App.entitlements -s "Developer ID Application: COMPANY"

.zip   sh app_notarizer.sh --notarize -a MyApp.app.zip  -b com.company.myapp  -u myappleaccount@gmail.com -p aaaa-aaaa-aaaa-aaa -v FFFFFFFF

.dmg   sh app_notarizer.sh --notarize -a MyApp.dmg  -b com.company.myapp  -u myappleaccount@gmail.com -p aaaa-aaaa-aaaa-aaa -v FFFFFFFF

Notarization request is created by executing the following command:

sh app_notarizer.sh --notarize -a <App Bundle Filename>  -b <Bundle identifier>  -u <Apple Developer ID Account> -p <App-specific password> -v <Access Provider> -e App.entitlements -i <Developer ID Application Certificate Name>

For this sample, I used this App.entitlements file. you can find more information on entitlements here.

If successful we get a response similar to this one :

<?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>notarization-upload</key>
	<dict>
		<key>RequestUUID</key>
		<string>39d40f6e-510b-497f-9edd-92ac4e128e54</string>
	</dict>
	<key>os-version</key>
	<string>10.15.4</string>
	<key>success-message</key>
	<string>No errors uploading 'MacNotarizationSample.app.zip'.</string>
	<key>tool-path</key>
	<string>/Applications/Xcode.app/Contents/SharedFrameworks/ContentDeliveryServices.framework/Versions/A/Frameworks/AppStoreService.framework</string>
	<key>tool-version</key>
	<string>4.01.1182</string>
</dict>
</plist>

We will use the RequestUUID (39d40f6e-510b-497f-9edd-92ac4e128e54 ) to check the status in the next step.

Check notarization request status

Syntax:  
[ -c | --check ] [ -u USERNAME ] [ -p PASSWORD ] [ -k UUID ] 

Parameters:           
[ -u ]   - Apple Developer ID Username           
[ -p ]   - Application Specific password
[ -k ]   - Notarization Request UUID 

Example:           

sh app_notarizer.sh --check  -u myappleaccount@gmail.com -p aaaa-aaaa-aaaa-aaa -k ffff-ffffff-ffffff-ffffffffff

We can check the notarization request status by using the following command:

sh app_notarizer.sh --check  u <Apple Developer ID Account> -p <App-specific password> -k <Notarization Request UUID>

It might take a few minutes to change from processing to approved or invalid. But if approved here is the response we get:

<?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>notarization-info</key>
	<dict>
		<key>Date</key>
		<date>2020-05-27T23:25:30Z</date>
		<key>Hash</key>
		<string>db0cd6ffe1473a4fcf6878434da4ca3f87bddbaefe10c7d19fa0f1837d2923ce</string>
		<key>LogFileURL</key>
		<string>https://osxapps-ssl.itunes.apple.com/itunes-assets/Enigma123/v4/b2/9d/58/b29d58d0-f682-3350-cf92-6839dd53db40/developer_log.json?accessKey=1590816570_916475138143872773_0ekVqlob4RTDhmPbhhUd9fGyAUOZE%2FsBdaE%2FKfEXY4DV3Mk6mUy5ck5JCP6%2F%2BP1kcluqt21oVVs6xx7V6U%2BVj%2B%2Bf1K2tkLj0aPp91tEPRNzPfdbGG2TeHyJnI56MQ9FV%2Bqjbt%2B1zqXt%2FYMs4TSJOGZ9AosH1hx%2FpDMQhvqxempE%3D</string>
		<key>RequestUUID</key>
		<string>39d40f6e-510b-497f-9edd-92ac4e128e54</string>
		<key>Status</key>
		<string>success</string>
		<key>Status Code</key>
		<integer>0</integer>
		<key>Status Message</key>
		<string>Package Approved</string>
	</dict>
	<key>os-version</key>
	<string>10.15.4</string>
	<key>success-message</key>
	<string>No errors getting notarization info.</string>
	<key>tool-path</key>
	<string>/Applications/Xcode.app/Contents/SharedFrameworks/ContentDeliveryServices.framework/Versions/A/Frameworks/AppStoreService.framework</string>
	<key>tool-version</key>
	<string>4.01.1182</string>
</dict>
</plist>

Also should receive an email:

If you get invalid as status or run into any other issue please check: https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution/resolving_common_notarization_issues

Staple notarization

Finally, we need to staple the notarization to the application.

 Syntax:           
[ -s | --staple ] [ -a | --file APP_NAME ] 

Parameters:          
[ -a | --file  ]  - File name Example:           

sh app_notarizer.sh --staple --file MyApp.app

We can do it with the following command:

sh app_notarizer.sh --staple --file MacNotarizationSample.app

Now we can successfully distribute our .NET Core console application!

Special thanks to Lorenzo Araujo who helped me create this amazing script.

You can find the source code and notarization script of this sample here.

Happy macOS Notarization!

References

https://developer.apple.com/developer-id/

https://devblogs.microsoft.com/xamarin/macos-hardened-runtime-notary/

https://docs.microsoft.com/en-us/dotnet/core/install/macos-notarization-issues

https://docs.microsoft.com/en-us/dotnet/core/deploying/

https://damian.fyi/2019/07/30/build-notarize-repeat/