Recipe OSX Code Signing

Mat Kelly edited this page Sep 23, 2015 · 1 revision

App bundles created by PyInstaller can be code signed on OS X.

Create self-signed certificate for codesign

  1. Use Apple Keychain utility: Open Applications > Utilities > Keychain Access.
  2. From the Keychain Access menu, choose Certificate Assistant > Create a Certificate.
  3. Pick up a unique name for the certificate e.g. Code Signing Test
  4. Follow the rest of the wizard or the Apple Code Signing Guide.

Sign an App Bundle

  1. codesign tool requires the signing certificate to be present in the Keychain.
  2. Find out the certificate Common Name that you used for creating the certificate. In my case it was Code Signing Test.
  3. Make sure Info.plist contains items CFBundleIdentifier and CFBundleName. These two are essential for codesign. You could use PyInstaller option --osx-bundle-identifier to specify the identifier. By default it is the first script's basename. They should look like this:
<plist version="1.0">
  1. Use your certificate name in the following command to sign the .app bundle that pyinstaller created:
codesign -s "Code Signing Test"

Common Issues

Some common error messages of codesign utility and how to solve them.

code object is not signed at all

User interaction is not allowed.

  • This basically means that codesign utility is not allowed to access certificates stored in Keychain.

  • Enable access to Keychain:

security unlock-keychain ${HOME}/Library/Keychains/login.keychain

* Verifies access to Keychain:
security show-keychain-info ${HOME}/Library/Keychains/login.keychain

PyInstaller Fix Implementation

PyInstaller breaks OSX code signing because it appends python code at the end of the binary. Appending data at the end of executable breaks the Mach-o format structure. codesign utility complains with the following messages.

the __LINKEDIT segment does not cover the end of the file (can't be processed)

file not in an order that can be processed (link edit information does not fill the __LINKEDIT segment)

  • Fix __LINKEDIT - File Size (offset + File size == exe size), VM Size- same as 'File Size'
  • Fix LC_SYMTAB - String Table Size - last data in mach-o file (offset + size = exe size on the filesystem) - The data appended to the executable will be part of the 'String Table' (Last data section in Mach-O file).
  • Example of using macholib library.
from macholib.MachO import MachO

exe_data = MachO('executable')
# Fat binary could contain multiple architectures.
for h in exe_data.headers:
    # Access Mach-O load commands
    for c in h.commands:
        # 'c' is a tupple (command_metadata, segment, [section1, section2])
    # The 4th (last) 'LC_SEGMENT_64' is the __LINKEDIT segment.
    linkedit = h.commands[3]

# Change some header parameters.
# Write changes back.
file_object = open(exe_data.filename, 'rb+')
  • At run-time the bootloader has to skip the signature segment that is appended at the end of exe file by codesign tool.

Other Links

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.