Code Signing Tasks

This chapter gives procedures and examples for the code signing process. It covers what you need to do before you begin to sign code, how to sign code, and how to ship the code you signed.

Obtaining a Signing Identity

To sign code, you need a code signing identity, which is a private key plus a digital certificate. The digital certificate must have a usage extension that enables it to be used for signing and it must contain the public key that corresponds to the private key. You can use more than one signing identity, each for its own purpose, such as one to be used for beta seeds and one for final, released products. However, most organizations use only one identity.

You can obtain two types of certificates from Apple using the developer portal: Developer ID certificates (for public distribution) and distribution certificates (for submitting to the Mac App Store). To learn more about this, read Tools Workflow Guide for Mac.

If you do not have an existing identity, you should first create one using the Certificate Assistant, which is provided as part of the Keychain Access application. This tool creates a public key, puts it into your keychain, and optionally can produce a certificate signing request that you can then send to Apple (or another certificate authority). The certificate authority then sends you a certificate that, in combination with your private key, completes your digital identity.

bullet
To import a signing certificate with Keychain Access
  1. In Keychain Access (available in /Applications/Utilities), choose File > Import Items.

  2. Choose a destination keychain for the identity.

  3. Choose the certificate file.

  4. Click Open.

Before you obtain a code signing identity and sign your code, consider the following points:

bullet
To use the Certificate Assistant to create a self-signed signing identity
  1. Open Applications > Utilities > Keychain Access.

  2. From the Keychain Access menu, choose Certificate Assistant > Create a Certificate.

    ../Art/create_cert1.jpg
  3. Fill in a name for the certificate. This name appears in the Keychain Access utility as the name of the certificate.

  4. Choose Self Signed Root from the Type popup menu.

  5. Check the Let me override defaults checkbox. Click Continue.

    ../Art/create_cert2.jpg
  6. Specify a serial number for the certificate. Any number will do as long as you have no other certificate with the same name and serial number.

  7. Choose Code Signing from the Certificate Type popup menu. Click Continue.

    ../Art/create_cert3.jpg
  8. Fill in the information for the certificate. Click Continue.

  9. Accept the defaults for the rest of the dialogs.

Adding an Info.plist to Single-File Tools

As discussed in “Code Requirements,” the system often uses the Info.plist file of an application bundle to determine the code’s designated requirement. Although single-file tools don’t normally have an Info.plist, you can add one. To do so, use the following procedure:

  1. Add an Info.plist file to your project (including adding it to your source control).

  2. Make sure the Info.plist file has the following keys:

    • CFBundleIdentifier

    • CFBundleName

  3. The value for CFBundleIdentifier is used as the default unique name of your program for Code Signing purposes. Because the CFBundleIdentifier value is also used when your application accesses resources in the application bundle, it may sometimes be necessary to use a non-unique CFBundleIdentifier value for a helper. If you do this, you must provide a different, unique identifier for code signing purposes by passing the -i or --identifier flag to the codesign command.

    The identifier used for signing must be globally unique. To ensure uniqueness, you should include your company’s name in the value. The usual form for this identifier is a hierarchical name in reverse DNS notation, starting with the top level domain, followed by the company name, followed by the organization within the company, and ending with the product name. For example, the CFBundleIdentifier value for the codesign command is com.apple.security.codesign.

  4. The value for CFBundleName shows up in system dialogs as the name of your program, so it should match your marketing name for the product.

  5. Add the following arguments to your linker flags:

    -sectcreate __TEXT __info_plist Info.plist_path

    where Info.plist_path is the complete path of the Info.plist file in your project.

    In Xcode, for example, you would add these linker flags to the OTHER_LDFLAGS build variable (Other Linker Flags in the target’s build rules).

For example, here are the contents of the Info.plist file for the codesign command:

<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleIdentifier</key>
    <string>com.apple.security.codesign</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>codesign</string>
    <key>CFBundleVersion</key>
    <string>0.3</string>
</dict>
</plist>

Signing Your Code

You use the codesign command to sign your code. This section discusses what to sign and gives some examples of the use of codesign. See the codesign(1) manual page for a complete description of its use.

What to Sign

You should sign every executable in your product, including applications, tools, hidden helper tools, utilities and so forth. Signing an application bundle covers its resources, but not its subcomponents such as tools and sub-bundles. Each of these must be signed independently.

If your application consists of a big UI part with one or more little helper tools that try to present a single face to the user, you can make them indistinguishable to code signing by giving them all the exact same code signing identifier. (You can do that by making sure that they all have the same CFBundleIdentifier value in their Info.plist, or by using the -i option in the codesign command, to assign the same identifier.) In that case, all your program components have access to the same keychain items and validate as the same program. Do this only if the programs involved are truly meant to form a single entity, with no distinctions made.

A universal binary (bundle or tool) automatically has individual signatures applied to each architecture component. These are independent, and usually only the native architecture on the end user's system is verified.

In the case of installer packages (.pkg and .mpkg bundles), everything is implicitly signed: The CPIO archive containing the payload, the CPIO archive containing install scripts, and the bill of materials (BOM) each have a hash recorded in the XAR header, and that header in turn is signed. Therefore, if you modify an install script (for example) after the package has been signed, the signature will be invalid.

You may also want to sign your plug-ins and libraries. Although this is not currently required, it will be in the future, and there is no disadvantage to having signatures on these components.

Depending on the situation, codesign may add to your Mach-O executable file, add extended attributes to it, or create new files in your bundle's Contents directory. None of your other files is modified.

When to Sign

You can run codesign at any time on any system running OS X v10.5 or later, provided you have access to the signing identity. You can run it from a shell script phase in Xcode if you like, or as a step in your Makefile scripts, or anywhere else you find suitable. Signing is typically done as part of the product mastering process, after quality assurance work has been done. Avoid signing pre-final copies of your product so that no one can mistake a leaked or accidentally released incomplete version of your product for the real thing.

Your final signing must be done after you are done building your product, including any post-processing and assembly of bundle resources. Code signing detects any change to your program after signing, so if you make any changes at all after signing, your code will be rejected when an attempt is made to verify it. Sign your code before you package the product for delivery.

Because each architecture component is signed independently, it is all right to perform universal-binary operations (such as running the lipo command) on signed programs. The result will still be validly signed as long as you make no other changes.

Using the codesign Command

The codesign command is fully described in the codesign(1) manual page. This section provides some examples of common uses of the command. Note that your signing identity must be in a keychain for these commands to work.

Signing Code

To sign the code located at <code-path>, using the signing identity <identity>, use the following command:

codesign -s <identity> <code-path> …

The <code-path> value may be a bundle folder or a specific code binary. See “What to Sign” for more details.

The identity can be named with any (case sensitive) substring of the certificate's common name attribute, as long as the substring is unique throughout your keychains. (Signing identities are discussed in “Obtaining a Signing Identity.”)

As is typical of Unix-style commands, this command gives no confirmation of success. To get some feedback, include the -v option:

codesign -s <identity> -v <code-path> …

Use the -r option to specify an internal requirement. With this option you can specify a text file containing the requirements, a precompiled requirements binary, or the actual requirement text prefixed with an equal sign (=). For example, to add an internal requirement that all libraries be signed by Apple, you could use the following option:

-r="library => anchor apple"

The code requirement language is described in “Code Signing Requirement Language.”

If you have built your own certificate hierarchy (perhaps using Certificate Assistant—see “Obtaining a Signing Identity”), and want to use your certificate’s anchor to form a designated requirement for your program, you could use the following command:

codesign -s signing-identity -r="designated => anchor /my/anchor/cert and identifier com.mycorp.myprog"

Note that the requirement source language accepts either an SHA1 hash of a certificate (for example H"abcd....") or a path to the DER encoded certificate in a file. It does not currently accept a reference to the certificate in a keychain, so you have to export the certificate before executing this command.

You can also use the csreq command to write the requirements out to a file, and then use the path to that file as the input value for the -r option in the codesign command. See the manual page for csreq(1) for more information on that command.

Here are some other samples of requirements:

  • anchor apple –the code is signed by Apple

  • anchor trusted –the anchor is trusted (for code signing) by the system

  • certificate leaf = /path/to/certificate –the leaf (signing) certificate is the one specified

  • certificate leaf = /path/to/certificate and identifier "com.mycorp.myprog" –the leaf certificate and program identifier are as specified

  • info[mykey] = myvalue – the Info.plist key mykey exists and has the value myvalue

Except for the explicit anchor trusted requirement, the system does not consult its trust settings database when verifying a code requirement. Therefore, as long as you don’t add this designated requirement to your code signature, the anchor certificate you use for signing your code does not have to be introduced to the user’s system for validation to succeed.

Adding Entitlements for Sandboxing

If you want to enable App Sandbox for an application, you must add an entitlement property list during the signing process. To do this, add the --entitlements flag and an appropriate property list. For example:

codesign --entitlements /path/to/entitlements.plist -s <identity> <code-path> …

For a list of entitlement keys that can appear in the entitlement property list, see Entitlement Key Reference.

Verifying Code

To verify the signature on a signed binary, use the -v option with no other options:

codesign -v <code-path> …

This checks that the code binaries at <code-path> are actually signed, that the signature is valid, that all the sealed components are unaltered, and that the whole thing passes some basic consistency checks. It does not by default check that the code satisfies any requirements except its own designated requirement. To check a particular requirement, use the -R option. For example, to check that the Apple Mail application is identified as Mail, signed by Apple, and secured with Apple’s root signing certificate, you could use the following command:

codesign -v -R="identifier com.apple.mail and anchor apple" /Applications/Mail.app

Note that, unlike the -r option, the -R option takes only a single requirement rather than a requirements collection (no => tags). Add one or more additional -v options to get details on the validation process.

If you pass a number rather than a path to the verify option, codesign takes the number to be the process ID (pid) of a running process, and performs dynamic validation instead.

Getting Information About Code Signatures

To get information about a code signature, use the -d option. For example, to output the code signature’s internal requirements to standard out, use the following command:

codesign -d -r code-path

Note that this option does not verify the signature.

Using the spctl Tool to Test Code Signing

The spctl(8) tool can be used to test your code signatures against various system policies that the user may set. The basic syntax for code signing assessment is shown below:

# Assess an application or tool
spctl --assess --type execute myTool
 
# Assess an installer package
spctl --assess --type install myInstallerPackage.pkg

If your application or package signature is valid, these tools exit silently with an exit status of 0. (Type echo $? to display the exit status of the last command.) If the signature is invalid, these tools print an error message and exit with a nonzero exit status.

For more detailed information about why the assessment failed, you can add the --verbose flag. For example:

spctl --assess --verbose=4 /bin/ls

This prints the following output:

    /bin/ls: accepted
    source=Apple System

To see everything the system has to say about an assessment, pass the --raw option. With this flag, the spctl tool prints a detailed assessment as a property list.

To whitelist a program (exactly as if the UI did it), type:

spctl --add --label mytest /some/program

The --label is an optional tag that you can add to your own rules. This tag allows you to remove the rule easily by typing:

spctl --remove --label mytest

Note that this removes all rules that match the label, which means that it is a handy way to clean up after testing. You can also temporarily suspend your rules by typing:

spctl --disable --label mytest

and reenable them later by typing:

spctl --enable --label mytest

To see a list of the current assessment rules, use the --list flag. For example:

spctl --list --type execute

The resulting list of rules might look like this:

    3[Apple System] P0 allow execute
        anchor apple
    4[Mac App Store] P0 allow execute
        anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9] exists
    5[Developer ID] P0 allow execute
        anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists
    7[UNLABELED] P0 allow execute [/var/tmp/firefly/RUN-FIREFLY-JOBS/test1.app]
        cdhash H"f34c03450da53c07ac69282089b68723327f278a"
    8[UNLABELED] P0 allow execute [/var/tmp/firefly/RUN-FIREFLY-JOBS/test1.app]
        identifier "org.tpatko.Run-Firefly-Job-X-Cores" and certificate root = H"5056a3983e3b7f44e17e3db8e483b35b6745b236"

Notice that the list above includes a number of predefined rules that describe the handling of certain classes of code. For example, rule 5 captures all applications signed by a Developer ID. You can disable those applications by typing:

spctl --disable --label "Developer ID"

This command tells the system to no longer allow execution of any Developer ID-signed applications that the user has not previously run. This is exactly what happens when you use the preference UI to switch to "Mac App Store only".

Each rule in the list has a unique number that can be used to address it. For example, if you type:

spctl --list --label "Developer ID"

you might get a list of rules that looks like this:

    5[Developer ID] P0 allow execute
        anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.13] exists
    6[Developer ID] P0 allow install
        anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] exists and certificate leaf[field.1.2.840.113635.100.6.1.14] exists

Notice that there are separate rules for execution (5) and installation (6), and you can enable and disable them separately. For example, to enable installation of new applications signed with a Developer ID, you can type:

spctl --enable --rule 6

Finally, spctl allows you to enable or disable the security assessment policy subsystem. By default, assessment is turned off, which means that missing or invalid code signatures do not prevent an application from launching. However, it is strongly recommended that you test your application with assessment enabled to ensure that your application works correctly.

To enable or disable assessment, issue one of the following commands.

sudo spctl --master-enable   # enables assessment
sudo spctl --master-disable  # disables assessment
spctl --status               # shows whether assessment is enabled

For more information, see the manual page for spctl(8).

Shipping and Updating Your Product

The only thing that matters to the code signing system is that the signed code installed on the user’s system identical to the code that you signed. It does not matter how you package, deliver, or install your product as long as you don’t introduce any changes into the product. Compression, encoding, encrypting, and even binary patching the code are all right as long as you end up with exactly what you started with. You can use any installer you like, as long as it doesn't write anything into the product as it installs it. Drag-installs are fine as well.

When you have qualified a new version of your product, sign it just as you signed the previous version, with the same identifier and the same designated requirement. The user’s system will consider the new version of your product to be the same program as the previous version. In particular, the keychain will not distinguish older and newer versions of your program as long as both were signed and the unique Identifier hasn't changed.

You can take a partial-update approach to revising your code on the user’s system. To do so, sign the new version as usual, then calculate the differences between the new and the old signed versions, and transmit the differences. Because the differences include the new signature data, the result of installing the changes on the end-user's system will be the newly signed version. You cannot patch a signed application in the field. If you do so, the system will notice that the application has changed and will invalidate the signature, and there is no way to re-validate or resign the application in the field.