iOS Code Signing

ios
code-signing
crashlytics
hockey-app
xcode

#1

NOTE This guide is superseded by the documentation here…

This guide will help you to set up automated deployment of your app, using CircleCI.

Before beginning, you should set up your app on CircleCI, and ensure that you have a green build with all tests passing.

TL;DR

  1. Upload your .P12 file in Project Settings > iOS Code Signing.
  2. Add your provisioning profile (.mobileprovision) file to your repo.
  3. Set GYM_CODE_SIGNING_IDENTITY to match your code-signing identity, ie "iPhone Distribution: Acme Inc.".
  4. Build with gym and deploy with ipa.

Background - Code Signing Process

The code-signing process for iOS apps can seem difficult at first, but once familiar with the concepts the process is less daunting.

There are 2 necessary items for signing an app:

  1. A provisioning profile (.mobileprovision)
  2. A corresponding developer private-key (.p12).

With these 2 files, and an XCode project the goal is to produce 2 files:

  1. A signed application (.ipa)
  2. A debug symbols archive (.app.dSYM.zip).

The .ipa file is the application file that can be distributed to your testers using a service such as Crashlytics Beta or Hockey App. The debug symbols file will be used by your distribution service of choice to produce call-stacks helping you to debug any crashes in your app that testers encounter.

OSX circle Keychain

When signing an app, Xcode will look in any keychains on the machine to find the necessary credentials.

When you build on CircleCI, a new keychain named circle.keychain is created. The password for this keychain is circle. This keychain is unlocked for the duration of the build, and it is added to the default search path, so any credentials stored in this keychain are available to Xcode.

Any P12 certificates that you upload to CircleCI in Project Settings will be added to circle.keychain before your build begins. Any Provisioning Profiles files that are found in your repository will also be added to circle.keychain.

1. Install Fastlane tools locally

This guide assumes that you are developing on a Mac running OSX. The first step is to install the Fastlane tools. If you are using the system Ruby, you can install using

sudo gem install fastlane

Or if you are using a Ruby installed using Homebrew you can install as the current user:

gem install fastlane

The tools take a few minutes to install, so a little patience is required. For more information check out the Fastlane installation guide.

2. Create and Upload a Code Signing Certificate

To create a code signing certificate and keys we will use the cert Fastlane tool. Open a terminal in the root of the repository and run the following commands:

$ mkdir certificates
$ cert --output_path certificates

This will create 3 files in the certificates directly, all named after the certificate. In my case the certificate is named NK5Z971JCM but this will be different for each project.

$ ls certificates

NK5Z971JCM.cer
NK5Z971JCM.certSigningRequest
NK5Z971JCM.p12

The .certSigningRequest and .cer files are not needed to sign your app using CircleCI. You can delete these files if you have no need for them. The .p12 file is the private ket for your code-signing identity.

You can now upload the .p12 file to your project on CircleCI in Project Settings > iOS Code Signing. When your build runs this p12 file will be added to the circle keychain automatically.

3. Create A Provisioning Profile

Use the sigh tool to create a new provisioning profile.

$ sigh

This will create a provisioning profile (a file with the .mobileprovision extension) in the root of your repository. You can move this file to any folder that you like, and then add the file to your repository.

During the build on CircleCI the provisioning profile will be found and installed for use by Xcode.

4. Find Your Code Signing Identity name

One your local machine run the following command to list the current code-signing identities installed on your machine.

$ security find-identity -p codesigning

You should see output like the following:

Policy: Code Signing
  Matching identities
  1) 0620A954DE3B589E378435B38B8D87B6C0436BB0 "iPhone Distribution: Acme Inc. (GL31ZZ3256)"
     1 identities found

  Valid identities only
  1) 0620A954DE3B589E378435B38B8D87B6C0436BB0 "iPhone Distribution: Acme Inc. (GL31ZZ3256)"
     1 valid identities found

The string with the form "iPhone Distribution: Acme Inc. (GL31ZZ3256)" is your code-signing identity. You might have more than one identity on your local machine, so choose the correct one for your app. You should add this to your circle.yml file as follows, so that it’s available during the build:

machine:
  environment:
    GYM_CODE_SIGNING_IDENTITY: "iPhone Distribution: Acme Inc. (GL31ZZ3256)"

4. Sign Your App on CircleCI

We recommend using Fastlane Gym to build a signed app. gym is pre-installed on our containers, so it’s easy to set up. The first step is to add a new deployment command in your circle.yml file.

machine:
  environment:
    GYM_CODE_SIGNING_IDENTITY: "iPhone Distribution: Acme Inc. (GL31ZZ3256)"

deployment:
  beta_distribution:
    branch: master
     commands:
       - gym

You should take a few minutes to read the documentation on deployments using CircleCI. The deployment stanza above instructs CircleCI to run the gym command on each successful build of the master branch. The beta_distribution is just a name for the deployment. You can use any name here.

You might need to pass additional parameters to gym to specify exactly which configuration to build, but in most cases these are not necessary. You should run gym locally specifying GYM_CODE_SIGNING_IDENTITY or --codesigning_identity until you get a signed app.

deployment:
  beta_distribution:
    branch: master
     commands:
       - gym --scheme "App" --workspace "App.xcworkspace"

Deployment

Once you have a signed app you are on the homeward stretch. Distributing the app is easy. At CircleCI, we recommend using shenzhen to distribute your app. Shenzhen supports many distribution services such as iTunes Connect, HockeyApp, Beta by Crashlytics and TestFairy.

Hockey App

Log in to Hockey app and create a new API token at https://rink.hockeyapp.net/manage/auth_tokens Your token will need at least upload permission to upload new builds to Hockey App. Give your new API token a name specific to CircleCI such as "CircleCI Distribution". Copy the token, and log into CircleCI and visit the Project Settings for your app. Create a new Environment Variable with the name HOCKEY_APP_TOKEN and paste the token as the value. You can now access this token during the build.

Modify the deployment section of your circle.yml as follows:

deployment:
  beta_distribution:
    branch: master
    commands:
      - gym
      - ipa distribute:hockeyapp 
          --token            "$HOCKEY_APP_TOKEN"
          --notes            "CircleCI build $CIRCLE_BUILD_NUM"
          --commit-sha       "$CIRCLE_SHA1"
          --build-server-url "$CIRCLE_BUILD_URL"
          --repository-url   "$CIRCLE_REPOSITORY_URL"

Beta By Crashlytics

First, we need to get some credentials. Log in to Fabric.io and visit your organization’s settings page.

Click on your organisation (CircleCI in the image above), and click on the API key and Build Secret links to reveal the items.

In CircleCI, navigate to your App’s Project Settings page, and under Environment Variables add 2 new items named CRASHLYTICS_API_KEY and CRASHLYTICS_SECRET, with the values you find on Crashlytics website.

You can then modify the deployment section of your circle.yml as follows:

deployment:
  beta_distribution:
    branch: master
    commands:
      - gym
      - ipa distribute:crashlytics
          --crashlytics_path Crashlytics.framework
          --api_token    "$CRASHLYTICS_API_KEY"
          --build_secret "$CRASHLYTICS_SECRET"

TestFairy

To set up your app on TestFairy first visit the preferences page in the TestFairy dashboard and navigate to the API Key section. Copy your API key and go to your App’s Project settings on CircleCI. Add a new Environment Variable named TESTFAIRY_API_KEY and paste in the API key from the TestFairy dashboard.

Next, you need to edit your circle.yml as follows:

deployment:
  beta_distribution:
    branch: master
    commands:
      - gym
      - ipa distribute:testfairy
          --key     "$TESTFAIRY_API_KEY"
          --comment "CircleCI build $CIRCLE_BUILD_URL"

Help – It Didn’t Work!

No valid signing identities

If you get an error like

Code Sign error: No code signing identities found: No valid signing identities (i.e. certificate and private key pair) matching the team ID “3EF4ATW3JB” were found.

or

Code Sign error: No code signing identities found: No valid signing identities (i.e. certificate and private key pair) were found.

Then xcode was not able to find the correct keychain to use. The first step to take is to example the output of the “Install Code Signing Credentials” step of the build on CircleCI. This is listed under “machine”. Expand the output and you should see output like the following:

In this example there is one valid code-signing identity, "iPhone Distribution: UTAH STREET LABS INC (GL92ZZ6423)". The goal is to pass this literal string to xcode. If you are building with Fastlane and gym, then you should ensure that you have your code-signing identity string in your circle.yml like this:

machine:
  environment:
    GYM_CODE_SIGNING_IDENTITY: "iPhone Distribution: Acme Inc. (GL31ZZ3256)"

Or that you pass this string to gym with the --codesigning_identity flag.

If you are calling xcodebuild directly you should pass your code-signing identity as the CODE_SIGN_IDENTITY= parameter, for example CODE_SIGN_IDENTITY="iPhone Distribution: Acme Inc. (GL31ZZ3256)".

No provisioning profiles matching an applicable signing identity were found

If you get an error message that says:

Code Sign error: No matching provisioning profiles found: No provisioning profiles matching an applicable signing identity were found.

This means that you have a valid P12 file, but there was no matching provisioning profile found. The first thing to check is that you have a valid provisioning profile file (.mobileprovision) checked in to your repository. Then take a look at the output from the “Install Code Signing Credentials” step of the build. It should no longer be saying "No provisioning profiles found in repository." The output shout give a list of the provisioning profiles that were found as follows:

Installed provisioning profile 616323a4-5216-42b0-b150-a128674ec52f (UTAH STREET LABS INC - CircleCI iOS Game - ["GL92ZZ6423"])
1 certificate imported.

My build fails with a timeout during code-signing

If your build fails with a timeout during the code-signing process, the issue is usually that the correct keychains are not unlocked, or the keychains are not added to the system search path. The first thing to check is the output of security list-keychains. You should add a step to your build to run security list-keychains immediately before the command that times out.

You should see the following:

$ security list-keychains
    "/Users/distiller/Library/Keychains/login.keychain"
    "/Users/distiller/Library/Keychains/circle.keychain"
    "/Library/Keychains/System.keychain"

You need to ensure that login.keychain and circle.keychain are in this list. If you are adding a custom keychain using your own commands, make sure that you are unlocking the keychain too. Make sure you are doing something like the following:

security unlock-keychain       -p circle circle.keychain
security set-keychain-settings -lut 7200 circle.keychain

What Does “Install Code Signing Credentials” Do?

The “Install Code Signing Credentials” step of OSX builds is where we prepare the container for code-signing before your builds runs.

  1. We create a new keychain called circle.keychain with password circle. We unlock this keychain and add it to the system search path so xcode can access it.
  2. We install any P12 certificates that you have uploaded in project settings to circle.keychain
  3. We look through your repo and find any provisioning profiles you have. We copy any profiles to ~/Library/MobileDevice/Provisioning Profiles, and we add any certificates in the provisioning profiles to circle.keychain (the public key in the provisioning profile should match the private key in the P12 file).
  4. We call security find-identity -p codesigning to list the keychains in the build output.

#2

#3

#4

It fail at secuirt import command. Seem that it can’t find circle.keychain, do I need to add this to the repo?

security import certificates/$CERT_ID.p12 -k circle.keychain -T /usr/bin/codesign

security import certificates/$CERT_ID.p12 -k circle.keychain -T /usr/bin/codesign returned exit code 1

security: SecKeychainItemImport: The specified keychain could not be found. Action failed: security import certificates/$CERT_ID.p12 -k circle.keychain -T /usr/bin/codesign

And look like there is one typo CERT_ID and $CERT_NAME should be the same right?


#5

@marc any thought on @sarunw’s issue?


#6

Hi Sarun, I replied to this using our in-app messenger because I had some questions about your project. Did you get this working?


#7

I’m getting a timeout on this step

$ security import certificates/$CERT_NAME.p12 -k circle.keychain -T /usr/bin/codesign

Anyone else experience this?


#8

Hi Dan,

Is your P12 password-protected? If so you should add your password as an env-var in the project settings, something like P12_PW and add -P $P12_PW to the security import invocation.

Marc


#9

Thank you for the write up. However, I am experiencing a slightly different problem. I am getting through the import process and then I am using Fastlane’s “sigh” command to pull in the appropriate mobileprovision. After the app is built I am getting this error:
"Code Sign error: No code signing identities found: No valid signing identities (i.e. certificate and private key pair) matching the team ID “XXXXXXXXX” were found."
I have export the cert and the p12 and checked them into the repo. Any help on this would be tremendously appreciated!


#10

Can you reach out to sayhi@circleci.com and I can take a look at your build? Thanks!


#11

hey @marc

I’m getting :
"Code Sign error: No provisioning profiles found: No non–expired provisioning profiles were found. "

while running

“sudo gym -p ‘platforms/ios/myproject.xcodeproj’ --scheme “MyScheme” --use_legacy_build_api --export_method enterprise”

I have p12 in ios settings and .mobileprovision uploaded to main repo
I’ve set GYM_CODE_SIGNING_IDENTITY
the ~/Library/MobileDevice/Provisioning\ Profiles folder has .mobileprovision files

command :
security find-identity -p codesigning
shows :
1 valid identities found
just before running gym
any ideas how to solve this?


#12

#13

The command ‘security’ its failing for me.
The environment you’re using to build the apps is OS X? mine seems to be Ubuntu, and it doesn’t have the ‘security’ command!
Thanks!


#14

In this case you will need to switch your builds to OS X containers. You can do it in the Project settings -> Experimental settings on CircleCI. Could you please check if that helps?


#15

Has anyone run into the issue where code-signing step only sometimes hangs forever vs all the time? Sometimes I run “Rebuild” and signing doesn’t hang.
Is it a memory/container issue?


#16

#17