Keychain Services Core Concepts

Most apps need to store sensitive user information at some point. Most commonly it's a service-specific credential like a password or authentication token, but it could also be an encryption key, expiration date, or really anything private to the user that shouldn't be easily accessible on the device. It's important to respect user privacy and only store this information if the app absolutely needs it, but when it is necessary, the keychain is the most secure built-in option for storing sensitive data.

The Keychain Services APIs are powerful, but the they're written in C and it can be difficult to know where to start. In this tutorial, we'll cover the general concepts and focus on the main use case for most apps, storing generic passwords.

Keychain Overview

The keychain APIs are part of the Security framework, which is available on all Apple platforms (iOS, macOS, tvOS, watchOS). Mac is the only platform with a user-facing app for the keychain, called Keychain Access, which you can use to view, edit, and add items.

The keychain can store five types of items:

1) Generic Password

A generic password is any sensitive user data you would want to store in the keychain. It can be much more than a string value, which is what you would normally think of as a "password". Anything that can be encoded as a Data value can be stored as a generic password.

2) Internet Password

An Internet password item is similar to a generic password, but with extra attributes specific to the Internet, such as server, security domain, port, protocol, and path.

3) Certificate

Certificates are used to create secure connections to a server and are issued and backed by a trusted third party known as the Certificate Authority. As a developer, you're already familiar with certificates and have probably added at least a few to your Mac keychain (your developer and distribution certificates are examples). The third party that issues developer certificates is the Apple Worldwide Developer Relations Certification Authority.

4) Crytographic Key

Public and private key pairs are what make certificates secure. They can also be used independently of certificates to establish secure connections.

5) Identity

An identity is just a combination of certificate and private key. The certificate / private key pair you use to submit apps to the App Store is an example of an identity.

Note: You may have seen the "Secure Notes" feature in Keychain Access. Under the hood, secure notes are just generic passwords. If you add a secure note and run the command security dump-keychain you will see that the note has the class "genp", which is the class for generic password items.

Generic Passwords

Required Attributes

There are two attributes required to store generic passwords: "service" and "account". Since the keychain can hold an unlimited number of passwords, these two attributes are what we need to differentiate them.

For an app that stores its authentication information as a generic password item, the app's bundle identifier is usually a good default for the service attribute. But a service could be anything. For example, if the app stores an access token for another service (e.g. Facebook, GitHub), the service attribute could just be the name of that service.

The account attribute should be a unique string associated with an account for the service. For example, an account could be an email address, username, or user ID.

Access Groups & Keychain Sharing

Access groups allow users to share keychain items between apps and with app extensions. With the introduction of new app extensions on iOS and Mac and more developers releasing apps that can be used with the same account, sharing keychain items has become a more common requirement.

Keychain Sharing is an entitlement you can add to your apps to enable this feature. When you add the entitlement, you can specify one or more access groups which can be passed in as an attribute when storing and retrieving items.

Accessibility

Accessiblity in the context of keychain services refers to the security level of stored items. The options range from always (least secure) to only when a passcode has been set and the device is unlocked (most secure).

Error Handling

One of the more frustrating things about working with the Security framework is error handling. Errors are represented as OSStatus values, which is a typealias for Int32. Documentation on some error codes can be hard to find, making it difficult to adequately handle errors. Fortunately, all of the common error codes are listed here and I have gone through and converted them into an enum in my open-source keychain framework, CodableKeychain (discussed more below).

iCloud Keychain

In addition to sharing keychain items between apps and with app extensions on the same device, you can also share them between devices using a combination of access groups and the "synchronizable" attribute. There are three main caveats:

On macOS, there are two methods for access control, Access Control Lists and Access Groups. If you specify the synchronizable attribute and use access groups as discussed in this tutorial, you will get the same behavior as on iOS. Otherwise, you can read more about Access Control Lists here.

Only password items (i.e. Generic Password and Internet Password) can be synchronized between devices (no certificates, cryptographic keys, or identities).

Most Keychain Item Accessiblity options can be set to "this device only". Items with an accessibility option that specificies "this device only" will not be synchronized.

You can read more about the synchronizable attribute in the documentation here.

CodableKeychain

As mentioned earlier, generic password items can store more than a string value. CodableKeychain is a framework I wrote that uses the new Codable protocol to store conforming types to the keychain. Its goal is to make storing generic password items, handling keychain errors, and supporting keychain sharing easy.

Getting started with keychain services can seem daunting at first. There's a lot to learn and the C APIs can be frustrating to work with in Swift. That's probably why so many keychain wrappers have been written over the years in both Swift and Objective-C. Hopefully this tutorial has helped clarify the main keychain concepts so that you can feel more comfortable using one of those libraries or writing your own!