Home > Automator, macOS, Payload-Free Package Creator, Xcode > Notarizing Automator applications

Notarizing Automator applications

Apple recently updated their notarization documentation to include this note:

Beginning in macOS 10.14.5, all new or updated kernel extensions and all software from developers new to distributing with Developer ID must be notarized in order to run. In a future version of macOS, notarization will be required by default for all software.

Screen Shot 2019 04 10 at 4 03 43 PM

The part about “notarization will be required by default for all software” made me think, because there are a few apps that I’ve written over the years that are still useful (at least to me). All of them were built using Automator, which meant that the usual Xcode-based ways of notarizing applications wasn’t going to work for me.

With assistance by folks in the MacAdmins Slack though, I was able to develop a process that allowed me to do the following:

  1. Codesign an Automator application
  2. Upload the application to Apple for notarization
  3. Attach the notarization to the application
  4. Verify that the notarization was attached and valid.

The documentation linked below was also very helpful in figuring out how to notarize using command line tools:

For more details, please see below the jump.

Pre-requisites

For notarization, you need the following things:

  1. Xcode 10 or later installed on your Mac.
  2. An Apple Developer Connection account
  3. A one-time password for your ADC account’s Apple ID

As an example to use for the process, I’m using an existing Automator application that I wrote a while back:

Notarization requires that the application in question be code signed and I had not yet done so for this application, so I needed to code sign my application first.

To do this, first clear the application of extended attributes by running a command like the one below:

sudo xattr -rc "/path/to/Application Name Here.app"

In my case, the command looked like this:

sudo xattr -rc "/Users/username/Desktop/Payload-Free Package Creator.app"

Once the application is ready for signing, run a command like the one below was run to code sign the application:

codesign --force --options runtime --deep --sign "Developer ID Application: Name Here (YG45FDT45F)" "/path/to/Application Name Here.app"

In my case, the command looked like this:

codesign --force --options runtime --deep --sign "Developer ID Application: Rich Trouton (XF95CST45F)" "/Users/username/Desktop/Payload-Free Package Creator.app"

Once signed, verify the signature using a command like the one below:

codesign -dv --verbose=4 "/path/to/Application Name Here.app"

In my case, the command looked like this:

codesign -dv --verbose=4 "/Users/username/Desktop/Payload-Free Package Creator.app"

Once finished, the output of the code signing looked like this:


computername:~ username$ sudo xattr -rc "/Users/username/Desktop/Payload-Free Package Creator.app"
Password:
computername:~ username$ codesign –force –options runtime –deep –sign "Developer ID Application: Rich Trouton (XF95CST45F)" "/Users/username/Desktop/Payload-Free Package Creator.app"
computername:~ username$ codesign -dv –verbose=4 "/Users/username/Desktop/Payload-Free Package Creator.app"
Executable=/Users/username/Desktop/Payload-Free Package Creator.app/Contents/MacOS/Application Stub
Identifier=com.apple.automator.Payload-FreePackageCreator
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=378 flags=0x10000(runtime) hashes=4+3 location=embedded
VersionPlatform=1
VersionMin=657920
VersionSDK=657920
Hash type=sha256 size=32
CandidateCDHash sha1=e8e508b4a9f6afe4ca74aa1e721978d43e812a06
CandidateCDHash sha256=a410264a896780b689a326d73968eb361892702c
Hash choices=sha1,sha256
Page size=4096
CDHash=a410264a896780b689a326d73968eb361892702c
Signature size=9054
Authority=Developer ID Application: Rich Trouton (XF95CST45F)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Apr 10, 2019 at 2:23:36 PM
Info.plist entries=31
TeamIdentifier=XF95CST45F
Runtime Version=10.10.0
Sealed Resources version=2 rules=13 files=36
Internal requirements count=1 size=208
computername:~ username$

view raw

gistfile1.txt

hosted with ❤ by GitHub

The next thing needed is to get the app ready for upload to Apple for notarization. For this, you’ll need to do two things:

  1. Have your one-time password for your ADC account ready.
  2. Compress your application inside of a .zip file

Screen Shot 2019 04 10 at 1 31 35 PM

Once your application has been compressed, run a command similar to the one below to upload it to Apple for notarization:

xcrun altool --notarize-app --primary-bundle-id "com.example.application.name" --username "adc_appleid_here" --password "adc_appleid_one_time_password_here" --file "/path/to/Application Name Here.zip"

In my case, the command looked like this:

xcrun altool --notarize-app --primary-bundle-id "com.apple.automator.Payload-FreePackageCreator" --username "adc_appleid_here" --password "one-time-password-goes-here" --file "/Users/username/Desktop/Payload-Free Package Creator.zip"

Once finished, the output of the notarization upload looked like this:


computername:~ username$ xcrun altool –notarize-app –primary-bundle-id "com.apple.automator.Payload-FreePackageCreator" –username "adc_appleid_here" –password "one-time-password-goes-here" –file "/Users/username/Desktop/Payload-Free Package Creator.zip"
2019-04-10 14:26:08.435 altool[39198:348433] No errors uploading '/Users/username/Desktop/Payload-Free Package Creator.zip'.
RequestUUID = be136ed3-3888-44e1-87ed-0e5c8c13cdb5
computername:~ username$

view raw

gistfile1.txt

hosted with ❤ by GitHub

To validate that the notarization is successful, run a command similar to the one below:

xcrun altool --notarization-info uuid-goes-here --username "adc_appleid_here" --password "one-time-password-goes-here"

In my case, the command looked like this:

xcrun altool --notarization-info be136ed3-3888-44e1-87ed-0e5c8c13cdb5 --username "adc_appleid_here" --password "one-time-password-goes-here"

Once finished, the output of the notarization validation looked like this:


computername:~ username$ xcrun altool –notarization-info be136ed3-3888-44e1-87ed-0e5c8c13cdb5 –username "adc_appleid_here" –password "one-time-password-goes-here"
2019-04-10 14:29:20.894 altool[39296:353688] No errors getting notarization info.
RequestUUID: be136ed3-3888-44e1-87ed-0e5c8c13cdb5
Date: 2019-04-10 18:26:09 +0000
Status: success
LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets/Enigma123/v4/a8/6f/a7/a86fa770-d553-1597-3e1d-48bb9c4f5f2e/developer_log.json?accessKey=1555115360_2635583872253219967_IJVPxumx10OG%2Bvds%2Biwi%2FcbwW1Yy4qvaLM7BGo4gxyiVHlt35YPsgh09cgGH%2F7UA9vF6duU7reRWq4TcoqxgBYS2Uwbock7TVnaD1%2BnR3LUxo1E5aQcQfhIW4bxt6S4aSEj22PbuucT0oTyGPLKiuV%2FoYO%2FT1TlHtETnCaJk9nc%3D
Status Code: 0
Status Message: Package Approved
computername:~ username$

view raw

gistfile1.txt

hosted with ❤ by GitHub

As part of the validation, a link to a log file is included. In my case, the log looks like this:


{
"logFormatVersion": 1,
"jobId": "be136ed3-3888-44e1-87ed-0e5c8c13cdb5",
"status": "Accepted",
"statusSummary": "Ready for distribution",
"statusCode": 0,
"archiveFilename": "Payload-Free_Package_Creator.zip",
"uploadDate": "2019-04-10T18:26:09Z",
"sha256": "bc3ac8c1cf7e70de274025f92078159392d654f030bb32375b3fed83b9af8e3e",
"ticketContents": [
{
"path": "Payload-Free_Package_Creator.zip/Payload-Free Package Creator.app/Contents/document.wflow",
"digestAlgorithm": "SHA-256",
"cdhash": "4634e03994cd0851f26a6f8cd6470a95b9696fe5",
"arch": "i386"
},
{
"path": "Payload-Free_Package_Creator.zip/Payload-Free Package Creator.app/Contents/document.wflow",
"digestAlgorithm": "SHA-256",
"cdhash": "4634e03994cd0851f26a6f8cd6470a95b9696fe5",
"arch": "x86_64"
},
{
"path": "Payload-Free_Package_Creator.zip/Payload-Free Package Creator.app/Contents/document.wflow",
"digestAlgorithm": "SHA-256",
"cdhash": "4634e03994cd0851f26a6f8cd6470a95b9696fe5",
"arch": "x86_64h"
},
{
"path": "Payload-Free_Package_Creator.zip/Payload-Free Package Creator.app",
"digestAlgorithm": "SHA-256",
"cdhash": "a410264a896780b689a326d73968eb361892702c",
"arch": "x86_64"
},
{
"path": "Payload-Free_Package_Creator.zip/Payload-Free Package Creator.app/Contents/MacOS/Application Stub",
"digestAlgorithm": "SHA-256",
"cdhash": "a410264a896780b689a326d73968eb361892702c",
"arch": "x86_64"
}
],
"issues": null
}

view raw

gistfile1.txt

hosted with ❤ by GitHub

Once the notarization has been generated for the app, the next step is to attach, or staple, the notarization to the app. To do this, run a command similar to the one below on your codesigned app:

xcrun stapler staple "/path/to/Application Name Here.app"

In my case, the command looked like this:

xcrun stapler staple "/Users/username/Desktop/Payload-Free Package Creator.app"

Once finished, the output of the stapling process looked like this:


computername:~ username$ xcrun stapler staple "/Users/username/Desktop/Payload-Free Package Creator.app"
Processing: /Users/username/Desktop/Payload-Free Package Creator.app
Processing: /Users/username/Desktop/Payload-Free Package Creator.app
The staple and validate action worked!
computername:~ username$

view raw

gistfile1.txt

hosted with ❤ by GitHub

The final step is to validate that the stapling was successful. To do this, run a command similar to the one below:

stapler validate -v "/path/to/Application Name Here.app"

In my case, the command looked like this:

stapler validate -v "/Users/username/Desktop/Payload-Free Package Creator.app"

Once finished, the output of the stapling validation looked like this:


computername:~ username$ stapler validate -v "/Users/username/Desktop/Payload-Free Package Creator.app"
Processing: /Users/username/Desktop/Payload-Free Package Creator.app
Properties are {
NSURLIsDirectoryKey = 1;
NSURLIsPackageKey = 1;
NSURLIsSymbolicLinkKey = 0;
NSURLLocalizedTypeDescriptionKey = Application;
NSURLTypeIdentifierKey = "com.apple.application-bundle";
"_NSURLIsApplicationKey" = 1;
}
Props are {
cdhash = <a410264a 896780b6 89a326d7 3968eb36 1892702c>;
digestAlgorithm = 2;
flags = 65536;
secureTimestamp = "2019-04-10 18:23:36 +0000";
signingId = "com.apple.automator.Payload-FreePackageCreator";
teamId = XF95CST45F;
}
JSON Data is {
records = (
{
recordName = "2/2/a410264a896780b689a326d73968eb361892702c";
}
);
}
Headers: {
"Content-Type" = "application/json";
}
Domain is api.apple-cloudkit.com
Response is <NSHTTPURLResponse: 0x7fbe9de29840> { URL: https://api.apple-cloudkit.com/database/1/com.apple.gk.ticket-delivery/production/public/records/lookup } { Status Code: 200, Headers {
"Apple-Originating-System" = (
UnknownOriginatingSystem
);
Connection = (
"keep-alive"
);
"Content-Encoding" = (
gzip
);
"Content-Type" = (
"application/json; charset=UTF-8"
);
Date = (
"Wed, 10 Apr 2019 18:33:04 GMT"
);
Server = (
"AppleHttpServer/70a91026"
);
"Strict-Transport-Security" = (
"max-age=31536000; includeSubDomains;"
);
"Transfer-Encoding" = (
Identity
);
Via = (
"xrail:st11p00ic-qugw02260301.me.com:8301:18H140:grp60",
"icloudedge:fr02p01ic-ztde010902:7401:19RC85:Frankfurt"
);
"X-Apple-CloudKit-Version" = (
"1.0"
);
"X-Apple-Request-UUID" = (
"79e314c2-7be3-4715-b880-73e4a5e9b5a4"
);
"X-Responding-Instance" = (
"ckdatabasews:16303401:st42p63ic-ztfb17170801:8201:1905B392:a7f3f78fb52"
);
"access-control-expose-headers" = (
"X-Apple-Request-UUID, X-Responding-Instance",
Via
);
"apple-seq" = (
0
);
"apple-tk" = (
false
);
} }
Size of data is 2845
JSON Response is: {
records = (
{
created = {
deviceID = 2;
timestamp = 1554920826358;
userRecordName = "_d28c74d190a3782e89496b0a13437fef";
};
deleted = 0;
fields = {
signedTicket = {
type = BYTES;
value = "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";
};
};
modified = {
deviceID = 2;
timestamp = 1554920826358;
userRecordName = "_d28c74d190a3782e89496b0a13437fef";
};
pluginFields = {
};
recordChangeTag = jubjpo8f;
recordName = "2/2/a410264a896780b689a326d73968eb361892702c";
recordType = DeveloperIDTicket;
}
);
}
Downloaded ticket has been stored at file:///var/folders/74/mp0fvtls5kn9b8yzzwr98y400000gn/T/79e314c2-7be3-4715-b880-73e4a5e9b5a4.ticket.
The validate action worked!
computername:~ username$

view raw

gistfile1.txt

hosted with ❤ by GitHub

Following notarization, Apple should send you a notification similar to the one shown below that your app has been notarized.

Screen Shot 2019 04 10 at 5 00 19 PM

  1. April 13, 2019 at 5:36 pm

    Great Instructions Rich!

    When I try to notarize an AppleScript app the code signing and the notarization succeed as you say but when I get to the stapling step the response I get to:

    xcrun stapler staple “/Volumes/HardDrive/MyApp.app”

    is:

    CloudKit query for MyApp.app (2/936578f9cf6dff6314bdebeba427cac9dab3f7e8) failed due to “record not found”.
    Could not find base64 encoded ticket in response for 2/936578f9cf6dff6314bdebeba427cac9dab3f7e8
    The staple and validate action failed! Error 65.

    • April 19, 2019 at 5:37 am

      You might check out this thread: . It involves an extra step of creating an entitlements file. In the example case, it’s just entitlements to send Apple events and load external frameworks.

      • davidnottage
        May 21, 2019 at 8:14 pm

        “You might check out this thread” I guess the link disappeared? What extra step is it?

      • sstanleyau
        May 21, 2019 at 11:12 pm

        It looks like the link has been eaten — it’s a thread on Apple’s Developer Forums. Things have moved on a bit, and now the easiest way is to use SD Notary from the makers of Script Debugger. (Disclaimer: I’m one of the authors.)

  2. April 18, 2019 at 4:11 pm

    Instead of creating a .zip, I created a .dmg and then signed it (without hardening)
    I used the signed .dmg to upload for notarization and was then able to staple the successful notarization to the .dmg.
    So maybe that is the way to deal with AppleScript apps.

  3. cashxx
    April 22, 2019 at 8:12 pm

    macOS is slowly becoming iOS! Sad days ahead!

  4. Ni Liu
    May 31, 2019 at 2:51 am

    Hello, I want to ask, what is the difference between ADC and normal Developer ID? Thanks

  5. Lewis_Ting
    October 16, 2019 at 10:35 am

    do you have an automated terminal script for this process?

  6. redC
    June 25, 2020 at 5:24 pm

    thank you so much for putting this together and documenting it so well.

  7. November 23, 2020 at 4:48 pm

    Amazing. Thank you so much for this guide! Very helpful.

    Keep in mind that you can’t create “App-specific passwords” using a “Managed” Apple ID. This only works with “unmanaged” Apple IDs.

  8. September 4, 2021 at 3:44 pm

    Very clear, thank you so much. In my case, since my Apple ID is associated with multiple teams, in the notarization step I had to add the flag –asc-provider to specify the *shortname* for the team. This shortname can be found with xcrun altool –list-providers and the username and one-time password mentioned in the instructions.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: