Mac standalone codesigning: 2021 update


    Apr 07 2021 | 1:02 am
    In what seems like an annual tradition now, it is once again time to wrestle with Apple and figure out the hoops one needs to jump through to properly prepare a standalone for distribution on the Mac platform.
    This post is NOT going to be an exhaustive series of steps required - I refer you to many previous posts on this topic, in particular the excellent Cycling 74 summary by @Ben Bracken from Sept 2019: https://cycling74.com/articles/max-8-1-mac-os-10-15-catalina-support-and-notarization
    Since that article was written, it seems that a new requirement is to codesign each and every binary embedded in the app bundle (the --deep flag for codesign does not seem to work as advertised). Folks at Cycling helped me with a script that seems to be working - it signs each and every binary in the bundle. This process seems to be working to allow for distributed apps to successfully run on Catalina (10.15) as well as Big Sur (11.x).
    See the Ruby script below, including the places at the top you'll need to modify it, shown IN CAPS. Copy everything between the ===== lines, and save it as a text file named "standalone-codesigning-script.rb":
    ==================================== require 'open3'
    authority = "Developer ID Application: YOUR CERTIFICATE NAME" entitlements = "PATH/TO/YOUR-APP-NAME.entitlements" appbundle = "PATH/TO/YOUR-APP-NAME.app" appname = "YOUR-APP-NAME" resources = []; count = 1;
    # codesign the stuff in C74 folder Dir.glob("#{appbundle}/Contents/Resources/C74/**/*.{mxo,dylib,framework,bundle}") do |f| if !File.symlink?(f) resources.push(f) end end
    resources.each do |resource| puts count.to_s + ": " + resource cmd = "codesign -s \"#{authority}\" --timestamp --deep -f \"#{resource}\"" stdout, stderr, status = Open3.capture3(cmd) raise stderr unless status.success? end
    resources.clear
    # codesign the stuff in Frameworks folder Dir.glob("#{appbundle}/Contents/Frameworks/**/*.{mxo,dylib,framework,bundle}") do |f| if !File.symlink?(f) resources.push(f) end end
    resources.each do |resource| puts count.to_s + ": " + resource cmd = "codesign -s \"#{authority}\" --timestamp --deep -f \"#{resource}\"" stdout, stderr, status = Open3.capture3(cmd) raise stderr unless status.success? end
    # codesign the Max executable cmd = "codesign -f -s \"#{authority}\" --timestamp --deep --options runtime --entitlements \"#{entitlements}\" \"#{appbundle}\"" stdout, stderr, status = Open3.capture3(cmd) raise stderr unless status.success? =======================================
    To run the script, open the Terminal and run this command:
    ruby PATH/TO/standalone-codesigning-script.rb PATH/TO/YOUR-APP-NAME.app
    If you get an error saying "resource fork, Finder information, or similar detritus not allowed", then run the following in the Terminal, and then try again: xattr -cr PATH/TO/YOUR-APP-NAME.app
    That should get you a properly signed application, which you should then proceed to notarize and staple, as outlined in the article linked to above.
    I hope this is helpful - good luck! Please know I offer no guarantees with this, and also please know I'm not a Ruby wizard - I had a lot of help with the above.

    • Apr 07 2021 | 7:35 am
      Dan, huge time saver! Thank You, B
    • Apr 07 2021 | 3:51 pm
      šŸ‘ Ā šŸ™ŒšŸ™
    • Apr 07 2021 | 6:47 pm
      Thanks for posting this!
    • Apr 07 2021 | 6:56 pm
      Thanks everyone. But the real credit goes to the Cycling folks who assisted on this.... I'm just the messenger! :-)
    • Apr 07 2021 | 7:49 pm
      when i remember right, you invested a lot of time in the past to share some procedures.
    • Apr 07 2021 | 8:06 pm
      True - though many of those are now irrelevant/incorrect, thanks to Apple, LOL.
    • Apr 07 2021 | 8:29 pm
      Last Christmas I gave you my heart But the very next day you gave it away This year, to save me from tears I'll give it to someone special
    • Nov 25 2021 | 5:37 pm
      Hey Dan.
      Here we go again with MacOS 12.0.1 Monterey. Codesigning the app breaks Miraweb -- no longer does it output the IP, it outputs "http://none:-1"
      I'm experimenting with entitlements, and haven't been able to narrow down what might be needed that wasn't needed in Big Sur.
      Figured I'd ask if you had any thoughts or ideas. I know this is pretty esoteric.
      Mike
    • Nov 25 2021 | 5:44 pm
      You're right Michael on the esoteric side of things, I'm afraid I have zero experience in codesigning an app that includes Mira. All I can suggest is perusing the various Apple documentation on entitlements; since Mira uses networking protocols, perhaps they've changed and/or tightened up something there?
      Also somewhat randomly, I noticed recently that there was a Mira package update, so just be sure you're using the latest...
      Sorry couldn't be of more help! Perhaps the Cyclists will chime in if they're on this thread....
    • Nov 25 2021 | 6:04 pm
      Figured it out Dan. Found the needle in a haystack by removing single strands of hay at a time.
      com.apple.security.app-sandbox
      That's the culprit. Kills Mira dead.
      Now I'm off to see if removing that entitlement will cause any other problems, but I wanted to share this bit of data.
      Thanks for the reply! Mike
    • Nov 27 2021 | 12:42 am
      If Iā€™m remembering correctly, that entitlement is only necessary if you plan on distributing your app on the Mac App Store.ā€¦
    • Nov 27 2021 | 12:44 am
      Yes, I came across this as well. B
    • Feb 19 2022 | 3:53 am
      com.apple.security.app-sandbox
      kills more than Mira. If your code signing is failing or as was in my case, destroying your app (deleting objects), try removing this from your entitlements. Thanks Dan and the Cycling 74 team for your work
    • Feb 23 2022 | 6:12 am
      Would anyone mind sharing their entitlements file here? My standalone crashes on an M1 (11.x and 12.x) when I drag an Mp3 onto [playlist~]. The standalone was build on an Intel (11.6.3), wav files work fine, also happens when built on an M1 with 12.x
      Source Audio suggested that in may be a privileges (entitlements issue)....
      dropped mp3 files onto playlist get converted into aiff and then played using converted file. Converted files land in max or standalone's cache folder. could it be privileges issue ?
      The app needs to play back audio files that a user drags in, save a .txt to wherever the user wants, and import a .txt file from wherever the user wants. My entitlements file current has: <key>com.apple.security.automation.apple-events<;/key> <true/> <key>com.apple.security.cs.allow-jit<;/key> <true/> <key>com.apple.security.cs.allow-unsigned-executable-memory<;/key> <true/> <key>com.apple.security.cs.disable-library-validation<;/key> <true/> <key>com.apple.security.device.audio-input<;/key> <true/> <key>com.apple.security.device.camera<;/key> <false/> <key>com.apple.security.get-task-allow<;/key> <true/> <key>com.apple.security.assets.music.read-only<;/key> <true/> <key>com.apple.security.assets.music.read-write<;/key> <true/> <key>com.apple.security.files.user-selected.read-write<;/key> <true/> <key>com.apple.security.cs.allow-dyld-environment-variables<;/key> <true/> <key>com.apple.security.network.client<;/key> <true/>
      I'm guessing I don't need both <key>com.apple.security.assets.music.read-write<;/key> and <key>com.apple.security.assets.music.read-only<;/key> ?
      Thank you.
    • Feb 23 2022 | 6:45 am
      @RIOTCHILD, I have used a python script (based on the ruby script in this post) to codesign/notarize my zipped standalone successfully for 3rd party distribution (i.e. non-app-store distribution). I have extended it a bit since then with some other related features.
      In any case, I have used a basic entitlements file to successfully notarize a standalone. It's in the project resources/entitlements folder and includes the following:
      "entitlements": { 
      "com.apple.security.automation.apple-events": True, 
      "com.apple.security.cs.allow-dyld-environment-variables": True,
      "com.apple.security.cs.allow-jit": True, 
      "com.apple.security.cs.allow-unsigned-executable-memory": True, "com.apple.security.cs.disable-library-validation": True, 
      "com.apple.security.device.audio-input": True,
      # "com.apple.security.device.microphone": True, 
      # "com.apple.security.app-sandbox": True,
      }
      Note the last two lines are commented out and were not used during code signing/notarization. Hope this is helpful.
    • Feb 23 2022 | 12:42 pm
      My entitlements file is pretty short. The app in question reads and plays an audio file from disk (but not .mp3). It also writes a preferences file to the standalone's folder in the ~/Library/Application Support/.
      <?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.allow-unsigned-executable-memory<;/key> <true/> <key>com.apple.security.cs.disable-library-validation<;/key> <true/> <key>com.apple.security.device.audio-input<;/key> <true/> </dict> </plist>
      I think Source Audio has likely identified the root cause of the problem. Perhaps figure out where exactly Max is temporarily storing the converted .mp3 file, and then see if that's in a location you need some special entitlement for? The detailed rundown of the entitlements at the end of this Cycling post has been helpful to me in the past, but not sure if there have been any changes on the Apple side since it was first posted? https://cycling74.com/articles/max-8-1-mac-os-10-15-catalina-support-and-notarization
    • Feb 23 2022 | 1:44 pm
      I don't use any system higher than HighSierra for work, and so can't say where Standalones store settings in Monterey & co. In case of Max itself, path to cache folder is ~/Library/Application Support/Cycling '74/Max 8/Settings/Cache (user lib) the thing is - if Standalone crashes before converting file, than it probably also failed to create Cache folder.
      Can one still give full disc access rights to apps in Monterey ?
    • Feb 23 2022 | 1:56 pm
      Can one still give full disc access rights to apps in Monterey ?
      Not sure - I took a look here and didn't see anything obvious: https://developer.apple.com/documentation/security/hardened_runtime
    • Mar 03 2022 | 11:14 am
      Hello there, I have some noob questions regarding signing of applications. I'm building a standalone on Catalina Intel machine that targets Big Sur and Monterey M1 machines, and tried every technique I could find on this forum to sign it. Last attempt was using Dan's ruby script. Unfortunately I only have access to my own computer, and the distant user who is testing my builds with Big Sur and Monterey is reporting the issues to me. At the moment when building with Max 8.1.11 everything is working well except the max-ble external used in the standalone. It surprinsingly loads fine, seems to perform a scan, but doesn't seem to have the right entitlements to discover BLE devices, that's why I'm trying to sign the whole standalone with bluetooth entitlements (plus the suggested ones). Whenever I sign it, it loads but doesn't display any window except the Max console, which doesn't tell anything. When building with Max 8.2.2, the standalone says "max-ble : incorrect architecture", so I didn't even try to sign it yet.
      So before I update to Big Sur or install a newer XCode to build a universal external (I'm using my own fork of max-ble at the moment), I'd like to know if I need to enroll in Apple's Developer plan to get a proper Developer ID Application certificate (I only have a free Apple Development certificate for now), or if the notarization process (which I didn't try yet) has become somehow mandatory. Thanks in advance for any insights, Cheers
    • Mar 03 2022 | 12:24 pm
      My memory is a bit foggy here, but I do think there was a change at some point along the way in how Cycling packages up the externals bundled in a standalone, I think to help with some of the signing stuff. I can't remember when that was though - it may very well have been between versions 8.1 and 8.2. So I'd probably suggest moving forward with building the 64 bit, universal binary of the external, and updating to Max 8.2 to be sure. Another thing I can't fully remember is what is necessary in terms of certificates. I'm pretty sure that the free certificate is not sufficient to be able to sign and notarize applications, but I could be wrong. I've always used my paid account. I'm sure if you dig into this on the Apple site you can probably figure this one out.
      Sorry I couldn't be more helpful!
    • Mar 03 2022 | 12:44 pm
      Thanks Dan for your prompt reply ! Actually this is very helpful. I will try to update XCode to 12.4 first, the most recent version I can install on Catalina which allows universal builds, and see how it goes with Max 8.2.2 At some point I guess I'll have to update to Big Sur and get myself a paid account to avoid further issues (couldn't find much info on Apple's site about free accounts limitations yet), but I'm quite in a hurry to get my standalone to work, so your advice sounds like a good strategy. Also, thanks everyone on this thread for the valuable information I could find here (which unfortunately doesn't help much in my situation at the moment). I'll report here whenever I make some progress
    • Mar 03 2022 | 11:27 pm
      Unsurprisingly, still not working at the moment when built with Max 8.2.2 using a universal build of max-ble
      this post seems to confirm that a paid account is needed for distribution Here is the relevant part : The code you copy from the Xcode archive is typically development-signed:% codesign -d -vv to-be-signed/Daemon ā€¦ Authority=Apple Development: ā€¦ ā€¦ To ship this code, you need to re-sign it for distribution. I just enrolled in the developer program and installed xcode 12.4, will see how far I can go now before updating to Big Sur
    • Mar 04 2022 | 10:57 am
      Still not working on a 2017 M1 macbook runninng latest Big Sur, built from my late 2015 Intel Catalina macbook with a proper "Developer ID Application" certificate ... Could anyone confirm that the recursive signing procedure with the ruby script is still valid ? Here is my entitlement file, in case it helps : <?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.automation.apple-events<;/key> <true/> <key>com.apple.security.cs.allow-dyld-environment-variables<;/key> <true/> <key>com.apple.security.cs.allow-jit<;/key> <true/> <key>com.apple.security.cs.allow-unsigned-executable-memory<;/key> <true/> <key>com.apple.security.cs.disable-library-validation<;/key> <true/> <key>com.apple.security.files.user-selected.read-write<;/key> <true/> <key>com.apple.security.device.audio-input<;/key> <true/> <key>com.apple.security.device.camera<;/key> <true/> <key>com.apple.security.device.bluetooth<;/key> <true/> </dict> </plist> Maybe I'm missing some important step. This is getting very tedious without a M1 machine at hand to perform my own tests. Thanks in advance for any feedback, at this point I'm clueless, I just hope I don't need to buy a new macbook to solve this problem.
      Next thing on my list ATM is to try to distribute a collective instead.
    • Mar 04 2022 | 12:44 pm
      Can you clarify exactly what is not working now. Is the signed/notarized/stapled application able to start and run on the M1-user's machine, but just not operate properly? Any messages in the Max console if so?
      Also, it's probably worth for your M1-user to check to be sure there are no pending requests in the System Preferences for granting of privileges to your app to do whatever it needs to. I'm not describing it very well, but hopefully you know what I'm alluding to.
    • Mar 04 2022 | 12:46 pm
      And yes, the Ruby script still works for me in my use cases.
    • Mar 04 2022 | 1:08 pm
      Of course, sorry for not being precise enough. The app crashes at launch and the log is not very meaningful (see below). Is the notarization / staple process mandatory ? I just signed the standalone and zipped it with ditto as suggested here before uploading it to my server. I'll suggest the user to check for privileges, though I'm not sure what privileges should be granted for the use of BLE devices ... I think I'll try to get a signed version not including max-ble to work first, maybe the crashing is totally unrelated The relevant log parts seem to be : ... System Integrity Protection: enabled
      Notes: Translocated Process
      Crashed Thread: 2 Dispatch queue: com.apple.root.default-qos
      Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY
      Termination Reason: Namespace TCC, Code 0x0 ... Thread 2 Crashed:: Dispatch queue: com.apple.root.default-qos 0 ??? 0x00007ffe9494e8f4 ??? 1 libsystem_kernel.dylib 0x00007fff2052f56e __abort_with_payload + 10 2 libsystem_kernel.dylib 0x00007fff20530fef abort_with_payload + 9 3 com.apple.TCC 0x00007fff24cf2f72 __TCC_CRASHING_DUE_TO_PRIVACY_VIOLATION__ + 163 4 com.apple.TCC 0x00007fff24cf37a3 __TCCAccessRequest_block_invoke.130 + 498 5 com.apple.TCC 0x00007fff24cf13c8 __tccd_send_message_block_invoke + 573 6 libxpc.dylib 0x00007fff20289a9c _xpc_connection_reply_callout + 36 7 libxpc.dylib 0x00007fff20289a24 _xpc_connection_call_reply_async + 69 8 libdispatch.dylib 0x00007fff20398870 _dispatch_client_callout3 + 8 9 libdispatch.dylib 0x00007fff203b1035 _dispatch_mach_msg_async_reply_invoke + 375 10 libdispatch.dylib 0x00007fff203a851c _dispatch_kevent_worker_thread + 1390 11 libsystem_pthread.dylib 0x00007fff2053f4a5 _pthread_wqthread + 386 12 libsystem_pthread.dylib 0x00007fff2053e42f start_wqthread + 15 Here is the full log :
      Thanks a lot !
    • Mar 04 2022 | 4:21 pm
      Here is some more news : - I made a version without the max-ble external, signed it with the same entitlements, and no crash - I notarized the version of the app including the max-ble external successfully, but it is still crashing with the same log So the problem seems to arise from the use of max-ble, as the app is supposed to pass the gatekeeper. The only reason I can think of right now is that I don't provide the right entitlements during the signing process. I will try to drop a line to support and see if I can get an answer.
    • Mar 04 2022 | 5:12 pm
      Yeah, it's feeling like this is definitely specific to your Bluetooth-based stuff - unfortunately which I have zero experience with on my end. Sorry and good luck, and please report back....
    • Mar 04 2022 | 5:17 pm
      No problem :) Huge thanks for your help, you got me closer to the solution anyway. I just sent an email to support, will report back here although the answer will probably be a little off-topic ...
    • Mar 06 2022 | 2:32 pm
      Update : got it to work ! I just needed to add the NSBluetoothAlwaysUsageDescription key to the standalone's Info.plist, and the notarized app finally opened. [max-ble] was still not discovering devices on Big Sur, so I updated my OS and XCode versions, and checked how a freshly built [max-ble] behaved in Max. Turns out that the issue was due to a patcher scripting workaround I made to force devices disconnection that was not causing this on Catalina, not sure why but I removed it and now everything seems to work as expected. A "disconnect" message still needs to be implemented in [max-ble] as a clean alternative to my nasty workaround to get the desired behaviour back, but for now the standalone behaves well enough. Thanks again for the help, and sorry for the noise Cheers
    • Mar 06 2022 | 10:13 pm
      That's great, glad you got it sorted out, and thanks for posting the solution here as well!