1. Configure your entitlements
A code signature can include entitlements — key-value pairs that grant an executable permission to use a service or technology. When macOS runs a process, it grants that process the entitlements that its executable’s code signature claims. For more information, see Entitlements.
Important
Don’t apply entitlements to library code. It doesn’t do anything useful and can prevent your code from running.
You apply entitlements to a main executable. If your main executable needs entitlements, create an .entitlements property list file, and add the key-value pairs for the entitlements that the executable claims.
If you build your product with Xcode, you might be able to use the .entitlementsfile that Xcode manages in your source code. If not, create the .entitlements file yourself.
Important
The entitlements file must be a property list in the standard XML format with LF line endings, no comments, and no byte-order mark (BOM). If you’re not sure if your file conforms to these requirements, use plutil to convert it to the standard format. For specific instructions, see Resolving common notarization issues.
If you have a development-signed version of your program, you can print its entitlements using the codesign command-line tool, and use that information as the basis for your entitlements property list file. For example:
% codesign -d --entitlements - --xml "to-be-signed/ConfigApp.app" | plutil -convert xml1 -o - -
…
<dict>
<key>com.apple.application-identifier</key>
<string>[Your Team ID].com.example.apple-samplecode.DaemonWithApp.App</string>
<key>com.apple.developer.team-identifier</key>
<string>[Your Team ID]</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>keychain-access-groups</key>
<array>
<string>[Your Team ID].com.example.apple-samplecode.DaemonWithApp.SharedKeychain</string>
</array>
</dict>
</plist>
If you use the entitlements from a development-signed version of your program to create the entitlements property list file for your distribution-signed code, consider the following changes to the entitlements:
-
Change the value of
APS Environment (macOS) Entitlementfromdevelopmenttoproduction. -
The
com.apple.security.get-task-allowentitlement allows the debugger to attach to your program, so you rarely apply it to a distribution-signed program. For more information, see Resolving common notarization issues.
For any other entitlement, see the documentation for that specific entitlement in Entitlements.
Embed distribution provisioning profiles
For security reasons, a provisioning profile must authorize most entitlement claims. For example, the keychain-access-groups entitlement must be authorized by a provisioning profile. This stops other developers from distributing apps that impersonate your app, to access its confidential keychain items.
macOS allows programs to claim certain entitlements without such authorization. These unrestricted entitlements include:
-
com.apple.security.get-task-allow -
com.apple.security.application-groups -
Those used to enable and configure the App Sandbox
-
Those used to configure the Hardened Runtime
If your program claims a restricted entitlement, include a distribution provisioning profile to authorize that claim:
-
Create the profile on the developer website. For more information, see Developer Account Help. Make sure to choose a profile type that matches your distribution channel (Mac App Store or Developer ID).
-
Copy that profile into your program’s bundle. For more information, see Placing content in a bundle.
If your product includes a nonbundled executable that uses a restricted entitlement, package that executable in an app-like structure. For more information, see Signing a daemon with a restricted entitlement.
In the DaemonWithApp example, the configuration app and its share extension use a keychain access group to share secrets. The system grants the programs access to that group based on their keychain-access-groups entitlement claim, and a provisioning profile must authorize such claims. The app and the share extension each have their own profile. To distribute the app, update the app and share extension bundles with the corresponding distribution provisioning profile:
% cp "ConfigApp-Dist.provisionprofile" "to-be-signed/ConfigApp.app/Contents/embedded.provisionprofile"
% cp "Share-Dist.provisionprofile" "to-be-signed/ConfigApp.app/Contents/PlugIns/Share.appex/Contents/embedded.provisionprofile"
Modifying the app in this way breaks the seal on its code signature. You re-sign the app before you distribute it.
Important
If you build your product with Xcode, then you might find that Xcode embeds a provisioning profile within your bundle. This is a development provisioning profile. Replace it with a distribution provisioning profile.
Confirm your code-signing identity
The code you copy from the Xcode archive is typically signed using a development code-signing identity:
% codesign -d -vv to-be-signed/Daemon
…
Authority=Apple Development: …
…
To ship a product that was signed for development, you need to re-sign it for distribution using an appropriate code-signing identity. Choose the correct identity for your distribution channel:
-
To distribute an app on the Mac App Store, use an Apple Distribution code-signing identity. This is named
Apple Distribution: <Team Name> (<Team ID>), where<Team Name>and<Team ID>identifies your team. -
To distribute a product independently, use a Developer ID Application code-signing identity. This is named
Developer ID Application: <Team ID>, where<Team ID>identifies your team.
For information on how to set up these code-signing identities, see Developer Account Help.
To confirm that your code-signing identity is present and correct, run the following command:
% security find-identity -p codesigning -v
1) A06E7F3F8237330EE15CB91BE1A511C00B853358 "Apple Distribution: …"
2) ADC03B244F4C1018384DCAFFC920F26136F6B59B "Developer ID Application: …"
2 valid identities found
The -p codesigning argument filters for code-signing identities. The -vargument filters for valid identities only. If the code-signing identity that you need isn’t listed, see Developer Account Help.
Each output line includes a SHA-1 hash that uniquely identifies the identity. If you have multiple identities with the same name, sign your code using this hash rather than the identity name.
Sign each code item
For all code types, the basic codesign command looks like this:
% codesign -s <CodeSigningIdentity> <PathToExecutable>
Replace <CodeSigningIdentity> with the name of the code-signing identity to use, and replace <PathToExecutable> with the path to the code to sign.
The specific identity you use for <CodeSigningIdentity> depends on your distribution channel, as discussed in “Confirm your code-signing identity”, above.
Note
If you have multiple identities with the same name, supply the identity’s SHA-1 hash to specify it unambiguously. For information on how to get this hash, see “Confirm your code-signing identity,” above.
If you’re re-signing code — that is, the code you’re signing is already signed — add the -f option.
If you’re signing a main executable that needs entitlements, add the --entitlements <entitlementsPath> option, where <entitlementsPath>is the path to the entitlements file that you created for that executable.
If you’re signing for Developer ID distribution, add the --timestamp option to include a secure timestamp.
If you’re signing a main executable for Developer ID distribution, add the -o runtime option to enable the Hardened Runtime. For more information about the Hardened Runtime, see Hardened Runtime.
If you’re signing nonbundled code, add the -i <BundleID> option to set the code-signing identifier, where <BundleID> is the bundle ID the code would have if it had a bundle ID. For example, for an app whose bundle ID is com.example.flying-animals that has a nested command-line tool called pig-jato, you could use com.example.flying-animals.pig-jato as the bundle ID for the command-line tool.
Note
For bundled code, you don’t need to supply a code-signing identifier because codesign defaults to using the bundle ID.
If you’re using a custom DR, add the -r <ReqPath> option, where <ReqPath> is the path to the requirements file containing the DR for this code item.
Repeat this signing step for every code item in your product, in the order you established in “Determine the signing order,” above. If you have a complex product with many code items to sign, create a script to automate this process.
Here’s the complete sequence of commands to sign the DaemonWithApp example for Developer ID distribution:
% codesign -s "Developer ID Application" -f --timestamp "to-be-signed/ConfigApp.app/Contents/Frameworks/Core.framework"
to-be-signed/ConfigApp.app/Contents/Frameworks/Core.framework: replacing existing signature
% codesign -s "Developer ID Application" -f --timestamp -o runtime --entitlements "Share.entitlements" "to-be-signed/ConfigApp.app/Contents/PlugIns/Share.appex"
to-be-signed/ConfigApp.app/Contents/PlugIns/Share.appex: replacing existing signature
% codesign -s "Developer ID Application" -f --timestamp -o runtime --entitlements "ConfigApp.entitlements" "to-be-signed/ConfigApp.app"
to-be-signed/ConfigApp.app: replacing existing signature
% codesign -s "Developer ID Application" -f --timestamp -o runtime -i "com.example.apple-samplecode.DaemonWithApp.Daemon" "to-be-signed/Daemon"
to-be-signed/Daemon: replacing existing signature
Important
Don’t run codesign using sudo because codesign relies on information in your user account when it signs code. Using tools like sudo to change user accounts causes problems when codesign tries to access this information.
Avoid deep code signing
Don’t pass the --deep option to codesign when you sign code. This option is helpful in some specific circumstances (for example, in verifying a code signature), but it causes problems when signing a complex product. Specifically:
-
The
--deepoption applies the same code-signing options to every code item that it signs. For example, if you have an app with an embedded command-line tool, where the app and the tool need different entitlements,codesign --deepapplies the same entitlements to both. -
codesignonly signs nested code when you use the--deepoption if that code is in particular locations in the top-level bundle. If you put code in a place wherecodesignexpects to find data,codesign --deepdoesn’t sign it. For information on the correct organization of nested code within a bundle, see Placing content in a bundle.