Skip to content

Instantly share code, notes, and snippets.

@bpteague
Forked from txoof/OS X Code Signing Pyinstaller.md
Last active February 21, 2025 01:05
Show Gist options
  • Save bpteague/750906b9a02094e7389427d308ba1002 to your computer and use it in GitHub Desktop.
Save bpteague/750906b9a02094e7389427d308ba1002 to your computer and use it in GitHub Desktop.
PyInstaller recipe for codesigning an OSX .app bundle
<?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>
<!-- These are required for binaries built by PyInstaller -->
<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/>
</dict>
</plist>

This worked as of Feb 17 2025, on OSX Sonoma 14.7.2, using PyInstaller 6.12.0. And this packaged a very nontrivial app, which you can now download from https://cytoflow.readthedocs.io.

Setup

  • Create a developer account with Apple
  • Download and install XCode from the App Store.
    • open XCode and install all of the command-line tools when it asks.
  • Create a certificate signing request (CSR)
    • Launch the "Keychain Access" utility
    • Choose Keychain Access > Certificate Assistant > Request a Certificate from a Certificate Authority.
    • In the Certificate Assistant dialog, enter an email address in the User Email Address field.
    • In the Common Name field, enter a name for the key.
    • Leave the CA Email Address field empty.
    • Choose “Saved to disk,” then click Continue. The file will be a .csr file.
  • Create a "Developer ID Application" certificate.
    • Browse to https://developer.apple.com/account
    • Open the "Certificates" page
    • Next to "Certificates", click the + icon
    • Choose "Developer ID Application" certificate type and click "Continue"
    • Upload the CSR file you saved earlier and click "Continue"
    • Download the resulting certificate. It will be a .cer file.
  • Add the certificate to the login keychain
    • Return to the Keychain Access utility
    • Under the "File" menu, choose "Import Items..."
    • Select the .cer file you just downloaded.
    • When asked which keychain to add it to, choose the "login" keychain.
  • Verify that the certificate is available
    • Open a Terminal
    • Type security find-identity -p basic -v and ensure that the certificate you just added is present. Note the hexadecimal hash of the identity certificate.
    • If your certificate doesn't show up:
      • go back to Keychain Access
      • open the login keychain and choose "Certificates."
      • Right-click on your Developer ID Application certicate and choose "Evaluate"
      • Leave "Generic" selected and click "Continue"
      • If you get an error that the certificate is invalid, look to see why. My issue was that Xcode was supposed to install the intermediate certificates .... but didn't. If that's the case, you can get them from https://www.apple.com/certificateauthority/
  • Make an app-specific password for notarytool
    • Follow these instructions from Apple
    • Save the app-specific password to a password manager or somewhere else safe.
  • Store the app-specific password in the keyring so you don't have to type it in each time you run notarytool
    • In Terminal, run xcrun notarytool store-credentials.
    • When prompted, give the profile a memorable name
    • When prompted, provide your Apple ID, the app-specific password, and your "development team" (which you can find at https://developer.apple.com/account under "Membership Details"
    • If (when) you forget what this profile name is, you can find it in Keychain Access -- search for com.apple.gke.notary.tool.

Build a .app with PyInstaller

  • Build your .app as usual (packaging .app bundles is beyond the scope of this gist). By default, PyInstaller signs it with an ad hoc key to satisfy Gatekeeper, but you can't distribute it like that. Make sure it opens when you run it with open <myapp>.app from the terminal.
  • Make sure that the entitlements.plist file attached to this gist is present in the build directory
  • Add the following to the .spec file:
    • to the EXE() call, add codesign_identity='ABC123', replacing ABC123 with the hexadecimal hash of the identity certificate from above.
    • to the EXE() call, add entitlements_file=entitlements.plist, pointing to the entitlements.plist file from this gist and adapting the path as necessary.
  • Rebuild your .app using PyInstaller with the code signing and entitlements settings.

Check that the codesigning worked

  • In the Terminal, in the directory containing your .app bundle, type codesign -vv --strict <myapp.app>. Check that codesign returns "valid on disk" and "satisfies its Designated Requirement."
  • If that's not the case, you can run codesign -vvv --strict <myapp.app> (note the extra v) and look for errors.

Zip the app for notarization

  • You can only submit .pkg, .dmg and .zip file for notarizing. And you can't use zip to zip up the .app bundle! Instead, we use ditto to maintain some necessary HFS metadata.
  • In Terminal, say ditto -c -k --sequesterRsrc --keepParent <myapp.app> <myapp.zip>

Submit for notarization

  • In Terminal, say xcrun notarytool submit <myapp.zip> --keychain-profile <profile> --wait. Use the profile name that you created above, surrounding it in quotes if necessary.
  • If this is your first submission, be prepared to wait, possibly several days. You can see past submissions with xcrun notarytool history, and you can use xcrun notarytool info <submission-id> to check on a submission status if you need to turn off your computer or close the terminal that notarytool is running in.
    • Apparently Apple builds some sort of profile the first time a developer submits something. Afterwards, notarization should only take seconds to minutes.
    • notarytool should return Accepted if notarization succeeded. If it didn't, you can use notarytool log <submission-id> to get a clue to what went wrong.

Staple the notarization to the app

  • In Terminal, say xcrun stapler staple <myapp.app>.
  • Check that it returns The staple and validate action worked!

Double-check that Gatekeeper thinks the app will run

  • In Terminal, say spctl -a -v <myapp.app>
  • Double-check that Gatekeeper returns "accepted"
  • If not, say spctl -a -vvv <myapp.app> and look for errors.

Check https://gist.github.com/txoof/0636835d3cc65245c6288b2374799c43 for useful resources -- I initially forked this from there.

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