iOS Code Signing - Part 2

Flora Lee
- San Francisco

Update (10/31/2016): We’ve written a newer blog post about how we test, integrate and deploy our iOS app. It complements the information here, and includes up-to-date details about our current process.

In Part 1 of this series, I talked about the necessary pieces of the iOS code signing puzzle, and common problems encountered with Xcode server. I left off at exporting and importing certificates into Xcode server’s hidden user because it ties in nicely with the topic for this post, signing builds on hosted servers. The two situations are similar—in both cases you are trying to sign builds on users or machines you cannot physically sign into.

This part two of a three part post on iOS Code Signing. Read part one here and part three here.

Hosted CI Server

Stitch Fix uses CircleCI for continuous integration. When we wanted to distribute our mobile builds through CircleCI, we ran into the issue of signing the builds. How were we supposed to give CircleCI access to our certificates and provisioning profiles?

It turns out a simple script solves the problem.

First you need to figure out exactly which certificates you need. Everyone will need the certificate that Apple issued to you. Here’s mine:

Apple-Provided Certificate

Export it from Keychain Access and name it apple.cer.

Next you will need your distribution certificate. You will export it twice, once just the certificate (dist.cer) and once the pair (dist.p12). When you export dist.p12, you will be asked to provide a password. Remember this password, let’s call it $YOUR_PASSWORD.

Finally, depending on how your scheme is set up, you may or may not need your development certificate. If you do, export it the same way as the distribution one above.

Importing them into the remote keychain is a matter of using the security command-line tool. This tool is also useful for debugging local issues with keychains and certificates. Type security help for a list of available commands.

The command we’ll be using is security import. It will look like this (note use of backslashes to continue long lines—this is so it formats on the blog):

> security -v import %cer_path% -k %keychain_path% \
           -T /usr/bin/codesign
> security -v import %p12_path% -k %keychain_path% \
           -P $YOUR_PASSWORD -T /usr/bin/codesign

If you are trying to import to Xcode Server’s keychain, the path should be /private/var/_xcsbuildd/Library/Keychains/login.keychain.

For Stitch Fix’s CircleCI deployment, we added our certificates and profiles to our repo and use a script to import them to the remote build server. In this case, it’s a good idea for security purposes to create a new keychain, then delete it once your deployment is complete.

> security create-keychain -p $KEYCHAIN_PASSWORD stitchfix.keychain

Then import the certificates and keys:

> security -v import ./scripts/apple.cer \
           -k ~/Library/Keychains/stitchfix.keychain \
           -T /usr/bin/codesign
> security -v import ./scripts/dist.cer \
           -k ~/Library/Keychains/stitchfix.keychain \
           -T /usr/bin/codesign
> security -v import ./scripts/dist.p12 \
           -k ~/Library/Keychains/stitchfix.keychain \
           -P $YOUR_PASSWORD -T /usr/bin/codesign
> security -v import ./scripts/dev.cer \
           -k ~/Library/Keychains/stitchfix.keychain \
           -T /usr/bin/codesign
> security -v import ./scripts/dev.p12 \
           -k ~/Library/Keychains/stitchfix.keychain \
           -P $YOUR_PASSWORD -T /usr/bin/codesign

There are a few more steps after this. First you need to list and unlock the new keychain:

> security -v list-keychain -s \
> security -v unlock-keychain -p $KEYCHAIN_PASSWORD \

Second, set it as the default keychain:

> security -v default-keychain -s ~/Library/Keychains/stitchfix.keychain

Lastly, and this is very important, you want to increase the keychain timeout. The reason you need to do this is because the password for this keychain differs from the login password, so when the default timeout (which is short) expires, OSX will pop up a dialog prompting you for the keychain password again. This will halt your build.

> security -v set-keychain-settings -lut 7200 ~/Library/Keychains/stitchfix.keychain

Don’t forget to copy the provisioning profiles:

> mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
> cp ./scripts/profile/* ~/Library/MobileDevice/Provisioning\ Profiles/

After the deployment is completed, we delete the keychain and remove our profiles with this script:

> security -v delete-keychain stitchfix.keychain
> rm -f ~/Library/MobileDevice/Provisioning\ Profiles/*

Shenzhen for Build and Deploy

Instead of struggling with xcodebuild, I highly recommend shenzhen for building and signing your archive. It also supports automated upload to a number of beta distribution systems.

All we needed to get shenzhen to build and sign our distribution app properly was to specify the workspace file, the scheme, and the build configuration.

So in summary, our continuous integration pipeline looks like this:

  1. Build for testing
  2. Run tests
  3. Run script to import certificates and profiles
  4. Build and archive for distribution
  5. Distribute
  6. Run script to remove certificates and profiles

Steps 1 and 2 are usually one xcodebuild or xctool command combined together. Steps 3 to 6 are the deployment portion and only happen if step 2 is green.

Shenzhen is also pretty good about looking for your signing identities in your keychains, so it eliminates the need to specify a particular keychain in your project file. This is good because using the CODE_SIGN_KEYCHAIN flag can cause some unexpected problems, as I will talk about in Part 3 of this series.

Tweet this post! Post on LinkedIn

Come Work with Us!

We’re a diverse team dedicated to building great products, and we’d love your help. Do you want to build amazing products with amazing peers? Join us!