Code signing in your favor

This is a repost of the original version from the extinct Bitrise tech blog (mid 2016). I wanted to keep this important piece to live longer.

Imagine the biggest pain in iOS delivery. I guess many of you are thinking about code signing now. But why is it so bad?

Code signing..

..is the process necessary for you to do as a developer to deliver your amazing applications to your users' iPhones, iPads, watches, TV-s or Macs. That is how you prove you are a certified third party and leave your fingerprint on all of your products.

In an ideal world..

..you would just open your Xcode and press play. This way in real life you will see an alert saying something is missing and you can let Xcode fix the issue, but usually it's not going to help.

You might even want to replace the "Fix Issue" button with @neonacho's to be on the safe side.

Also what happens if you are in a CI environment and have no idea which identity and profile to export and upload and, come on, how secure is that exactly?

There are several ways to solve all the code signing issues with different tools, so let me introduce some.

Fix you a code sign

What to do if you just don't know what went wrong or what to do to make Xcode archive that damn project?

fastlane tools

Last year's rocket ship in our open source world was definitely fastlane which is a collection of tools to make your iOS deployment process as easy as possible. Of course @KrauseFx has more solutions for the code signing hell as well, even on the team level. He created cert, sigh and match to create, install and share certificates and provisioning profiles.

match

It's the tool to fully automate managing certificates and provisioning profiles, optionally across teams, too. You are able to clear your development portal from invalid certificates and profiles, initialize new ones exactly for your needs and set them to your Xcode project. It stores the certificates and provisioning profiles in a separate repository.

Using it is easy as shooting a match init which creates a Matchfile . After adding the repository URL, the app id and your user's email address, you are able to run match. It will create the certificates and profiles, install and upload them to the repository. For more advanced setups, like handling different kind of profiles, check out the docs of fastlane.

cert & sigh

For full control over code signing and managing provisioning profiles there are two tools fastlane provides. cert can generate, download and install a new certificate for you.

sigh is able to renew, repair or create provisioning profiles for your project.

You can use cert and sigh together for full comfort.

You don't even need fastlane for using match, cert or sigh because these are all independent tools and they can be installed with gem install separately.

Life with CI

If you try to set up your project in a CI environment either self-hosted or third-party, you will probably face some difficulties. Keep in mind, these issues are not issues per se, they are all present to keep your code/app secure. But let's shed some light on what to do and how to set up correctly when working with CI.

SSH keys ๐Ÿ‘ฎ

Many of us have private repositories as dependencies in our projects, which is not a problem until you decide to use CI. Then you have to finally understand how all of this work and you will realize soon, it's no magic.

Deploy keys

It works the same on most hosted CI services: they ask you for permission to your code host during your app setup. This way they're able to generate a key and add it to your GitHub/Bitbucket/etc. as deploy key, probably read-only. The deploy key gives permissions to a single repository for the build machine having the SSH key installed only to deploy your code, so that's the most secure way.

Private dependencies

If you have references to private repositories (git submodules, CocoaPods, etc.), the CI has to clone them. To enable that, you would have to add another key to the machine, connected to the dependency's repository as deploy key. The problem is though, that the servers arenโ€™t able to choose between more than one key for the same host, so they will grab the first in the row and it might not be the right one.

You will have to deal with the same when using match from a CI environment, given it stores your certificates/profiles in a separate, private repository.

That's when the general SSH key comes to the rescue. You should probably remove the auto-generated deploy key from everywhere and generate a new key to be used for your project and its private dependencies. This key should be added to your GitHub/GitLab/etc. profile and your CI server as well. It means a bit more permissions given to your third party host, but still better than copying the SSH key from your local machine to the server. Anyway, there is no better solution right now.

Code signing & CI

The other and most common showstopper for your first green build is usually code signing, since you have to have a proper code signing identity and provisioning profile for your project installed on your CI machine if you'd like to automate your delivery.

If you'd like to use cert, sigh or match from a CI it's possible. For example if you are on Bitrise it's as easy as adding the fastlane step to your workflow and specifying the lane to run from your Fastfile. You can also run a simple script step where you install them as individual gems and run the desired command.

So it looks like if you want to use your existing identity and profile from your local machine, you'll have to export them somehow. But wait, how do I know which one to export from the 25 I have in my Keychain? ๐Ÿ”‘๐Ÿ”‘๐Ÿ”‘

DIY export

You are able to detect which identity and profile you are using within your project from the output of a simple basic xcodebuild clean archive.

It will grep the identities and profiles used and you can manually open your keychain and export the listed items if your Keychain is not as messed up as mine. Otherwise codesigndoc will be your friend.

Codesigndoc ๐Ÿš‘

One day @viktorbenei just got tired from watching our users suffering from code signing issues. He decided to make their lives easier. He created a command line tool, codesigndoc to export code signing identities and provisioning profiles needed for archiving their project with one command.

Usage

Run codesigndoc with the scan command and follow the steps. You can drag & drop the project or workspace file to the terminal to pass the path easily.

It will list your schemes and you can select which one to use.

codesign scan

codesigndoc will list your identity and provisioning profile used for archiving your app.

If you'd like to export them, it will pop up the Finder window with your exported .p12 and .provisionprofile/.mobileprovision file. Now you can easily upload these files to your favorite CI service/node.

codesigndoc export

How does it work?

If you are in the mood to hack the hell out of your Keychain, read on. An interesting story with good old C APIs and wrapping C code inside Go coming up.

Finding identities in the Keychain

First thing is to find an identity with the grepped label from the xcodebuild clean archive's output. With the help of the Keychain Services API it's all possible with some tricks. But wait, how boring life is without tricks? ๐Ÿค”

To find an item in the Keychain with it's name, you have to pass SecItemCopyMatching a dictionary of query parameters about what you actually want to find and then filter the results based on their label. Here we passed kSecClassIdentity as class for finding identities, set some flags, kSecReturnAttributes and kSecReturnRef to kCFBooleanTrue and let it give back all the results.

The result of this query will give you back all the identities so you can iterate through them and filter for the desired label, which is the name of the identity you are looking for. It's still possible to have more results after this, since you might have more, for example iOS Developer identities under the same name, either it's the one you use, or a revoked, or expired one. It also means you can export as many identities as you like into one .p12 file.

Exporting identities from the Keychain

You have to specify an array of items for export, a bunch of parameters but they're all empty in our case. Beware not to use nil as passphrase, it requires an empty string to be processed as empty passphrase. If everything goes well with SecItemExport you will have a CFDataRef pointing to your shiny exported identities! โœจ

Don't forget to release your references properly, there is no garbage collector around.

Validation check

You have to manually check whether the exported certificates are valid. First you have to turn the exported .p12 to X.509 certificate data, then you are able check if it's still valid. It only checks the expiration date.

How to C in Go

Easily. You can call C code from Go with cgo. Basically you can do your imports and set all your flags from comments in a simple go file. ๐Ÿ˜ฑ Yes, comments. ๐Ÿ˜ฑ

Let's check out our scan command's imports below.

Then you can just import "C" - which is not really a package, rather a reference to C's namespace - and go ahead writing your C code.

For example this is standard library's free function.

Also, since we have CoreFoundation imported above, that framework is also our friend now.

Neat! It's quite a challenge to find the proper types to use though, with that many languages in the mix.

Export provisioning profiles

It's pretty straightforward to find a profile based on its name. Given all profiles are stored under ~/Library/MobileDevice/Provisioning Profiles, finding them is as easy as searching for their UUID there. To support both iOS and Mac projects, prepare your script for .mobileprovision and .provisionprofile extensions.

What's next?

We should extend the certificate validator to check for revoked certificates. Now codesigndoc shows a warning if you have revoked certificates in your keychain but exports them along with your valid one into one .p12 file. It doesn't hurt if you copy it to your CI server, you also have them all in your Keychain.

It could use some UX tweaks, like automatic detection of project files in the current directory, or automatic selection of scheme if there's only one present.

Our passion is to build you tools to make your life easier, so please let us know what you miss in a comment below, a tweet, or a GitHub issue. PRs are always very welcome too. It would be great to turn it to a Homebrew formula, but let's leave it up to you if that's codesigndoc's future. ๐Ÿ˜‰

Send all the thanks to viktorbenei and godrei for implementing codesigndoc!