The Basics of a Shared Keychain

Reader level: Intermediate Technical

We, SPW’s iOS team, recently had a situation that required launching one iOS application from another, sharing data securely between the two. In iOS, URL schemes allow you to launch another app while passing in some basic information.

We decided that combining URL schemes with shared keychain access was the best way to securely transfer information between the apps. This post will cover the basics of shared keychain access.

It sounded easy but we’d never actually shared a keychain between different apps before, so my guard was up from the beginning. Like all things Apple, there’s a process. Miss a step in the process and it won’t work …hence this post 🙂

In the world of Mac/iOS, Keychain is a secure storage container that can be used for storing sensitive information such as passwords, certificates, authentication tokens and private keys. In reality, there’s the option of storing a CFDataRef (a Core Foundation data wrapper), so you can pretty much store whatever data you want. In iOS, you can just wrap it up as NSData and pass it through …perfect for our particular case.

All data stored would be encrypted with our own custom encryption algorithms, so even if a keychain item was compromised, the data retrieved would be useless without intimate knowledge of the encryption used and the keys required to decrypt.

In order to share keychain information between applications, we needed to setup a shared keychain access group. iOS devs will recognise that keychain groups are specified in your application’s target summary screen of Xcode. Scroll down to the ‘Entitlements’ section and you’ll see an option to use an entitlements file, as well as options for specifying keychain groups. In our projects, our default target already has a keychain group according to its bundle identifier.

For example:

App1

Bundle identifier: ASDFQWERTG.au.com.company.app1.

Keychain group: ASDFQWERTG.au.com.company.app1

App2

Bundle identifier: ASDFQWERTG.au.com.company.app2.

Keychain group: ASDFQWERTG.au.com.company.app2

What we needed was a new entry that could be accessed by multiple apps for this company. In both app1 and app2, we create a new shared keychain group: ASDFQWERTG.au.com.company.sharedaccess. You don’t need to add the app-identifier-prefix yourself, Xcode does this for you. If in doubt, check the raw .entitlements file yourself in a text editor. It should look like this (XML tags omitted):

For managing the keychain code itself, we use an open-source library called UICKeyChainStore (UICKeyChainStore). This is a helpful (and greatly appreciated) wrapper around Apple’s Keychain Services API that provides the basic CRUD operations we need for dealing with the keychain (including the ability to specify an access group and get to that shared keychain data we need).

To store data for a given key, it’s as simple as:

To retrieve data for a given key, it’s as simple as:

Gotchas

Simulator Lies

Apps that are built for the simulator are not signed, so there is no keychain access group. If you run the code from simulator between apps, everything works, you pat yourself on the back and go and play some golf with your buddies while confidently smiling that you are awesome, iOS is awesome and the day is so amazingly great. When you run it on a device however, everything stops working and you’re instantly under stress because something you thought was working has stopped dead with no apparent explanation.

Lesson learned:

Make sure you get a device for testing anything keychain related, especially if you’re using access groups. The device won’t lie, the simulator will.

Provisioning

The one thing that tripped me up was provisioning (surprise surprise). Before the sharing would work correctly, both apps needed to have provisioning based on the same app-identifier-prefix (aka your bundle seed ID …you know, those 10-uppercase-digits that appear before you bundle id).

To better explain this one, you need to understand provisioning basics. To get a development app onto a device, you would need to setup provisioning as follows:

You setup a certificate (Eg. ‘Your Company’).

You setup an app-id and a app-identifier-prefix (Eg. ASDFQWERTG.au.com.company.app1. NOTE: ASDFQWERTG is the app-identifier-prefix).

You setup a provisioning profile against an app-id and a certificate (Eg. ‘yourapp1 development provisioning’) and select devices that this provisioning profile is valid for.

You download and install the certificate to your Mac’s keychain, the provisioning profile to Xcode Organizer and assign the provisioning profile in Xcode for your project target.

Now when you do a development build, you have the certificate/private keys required to build an application for the given company. You also have the provisioning profile installed so Xcode can bundle it into your application and know what devices this app is valid for.

Lesson learned:

When sharing a keychain, be aware of the app-identifier-prefix and its role with multiple applications that are sharing keychain information.

Conclusions

Avoid using the simulator to debug, check your provisioning is set up correctly for shared keychain access groups and remember that Apple’s Keychain Services Reference is your friend. Read it carefully to avoid any dramas: Keychain Services Reference