If you’ve ever built an iOS app, you’ve probably came across a situation when you had to store sensitive information on behalf of the user. For this, Apple’s Keychain Services is your guy.

Lock

🔒 Keychain Services has been part of Apple’s Security frameworks since the very first public version of iOS SDK (iOS 2.0). Unlike UserDefaults, Keychain gives you a secure way to store passwords, credit card information or even notes in an encrypted database.

In the early days of my career, I was told to use a framework to wrap Keychain Services because of the unfriendly API Apple provides. Although I was quite happy with the interface provided by the 3rd party frameworks at the time, I wanted to find out the ins and outs of the service offered by the platform. You can read about my learning below.

Under the hood

Keychain Services consists of two main parts: an encrypted database (represented by the SecKeychain class) and items inserted into the database (represented by the SecKeychainItem class).

In iOS, there is only one keychain store (which encapsulates your iCloud keychain) and there’s no API to create or manipulate the default keychain other than CRUD operations. The keychain is automatically and simultaneously locked and unlocked with the device, so no unauthorized access is possible.

In Mac OS, on the other hand, you can create and manage your own Keychain stores (it creates 4 at installation: login, iCloud, System, System Roots - explore them in the Keychain Access application).

Keychain items

SecKeychainItems are opaque types hidden inside the Security framework, consisting of two parts: a data and a set of attributes which help to identify and store metadata or control access to your stored items. When a new item is about to be inserted, Keychain Services encrypts the data first, then wraps it together with the public attributes. See the illustration below.

How Keychain work

Since SecKeychainItems are hidden from the curious eyes, Apple provides a set of API’s to interact with the keychain using the CFDictionary type.

You can add, modify, delete and search for a specific keychain item or even modify its access control.

func SecItemAdd(_ attributes: CFDictionary, _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus
func SecItemDelete(_ query: CFDictionary) -> OSStatus

Item classes

These are special kinds of attributes that define how a keychain item is treated. As of 2018, there are 5 types of item classes:

Each class supports a special set of attributes. You can check them out by clicking on the provided links above.

Item attributes

Apple defines a list of key-value pairs that help you set attributes for your keychain item using a dictionary. These key-value items are categorized by the keychain item types mentioned above:

  • General Item Attribute Keys
  • Password Attribute Keys
  • Certificate Attribute Keys
  • Cryptographic Key Attribute Keys
  • Authentication Type Values
  • Key Class Values
  • Synchronizability Values
  • Many more …

Managing a keychain items

Now, to help you better understand this, I’ll show you how an internet password is stored in keychain.

let server = "example.com"
let username = "username"
let password = "password".data(using: .utf8)!
let attributes: [String: Any] = [
    (kSecClass as String): kSecClassInternetPassword,
    (kSecAttrServer as String): server,
    (kSecAttrAccount as String): username,
    (kSecValueData as String): password]

// Let's add the item to the Keychain! 😄
SecItemAdd(attributes as CFDictionary, nil) == noErr

Observe that I used the password as the data for the keychain item and three more attributes to provide additional info. In my example kSecClass tells what type of item we’d like to store in keychain, kSecAttrServer is the server address attribute which is specific to internet password type items and kSecAttrAccount holds the account for the specific internet service.

Once your data is stored securely inside keychain, you can query these items by their attributes. Storing sufficient information about a particular element will help you narrow down the search results to the one you’re looking for. Using kSecAttrLabel you can also tag your item to better find it later.

Let’s see how can you retrieve the data.

let query: [String: Any] = [
    (kSecClass as String): kSecClassInternetPassword,
    (kSecAttrServer as String): "example.com",
    (kSecMatchLimit as String): kSecMatchLimitOne,
    (kSecReturnAttributes as String): true,
    (kSecReturnData as String): true]
var item: CFTypeRef?

// should succeed
SecItemCopyMatching(query as CFDictionary, &item) == noErr

if let item = item as? [String: Any],
    let username = existingItem[kSecAttrAccount as String] as? String,
    let passwordData = existingItem[kSecValueData as String] as? Data,
    let password = String(data: passwordData, encoding: .utf8) {
    print("\(username) - \(password)")
}

To update or delete items, you can use a simpler query without asking Keychain to return any attributes (kSecReturnAttributes, kSecReturnData). Let’s say you want to change your password in keychain. Here’s how you do it:

let query: [String: Any] = [
    (kSecClass as String): kSecClassInternetPassword,
    (kSecAttrServer as String): "example.com",
    (kSecAttrAccount as String): "username"]
let attributes: [String: Any] = [kSecValueData as String: password]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)

⚠️ When you perform update or delete operations, be careful to filter down your query result to a single element as other applications or users might share the same credentials.

Now back to you

With a good understanding of Keychain, you’ll come to realize how implementing your own custom wrapper in your project is actually pretty easy and beneficial. It can speed up build time as well as reduce app size by avoiding an extra 3rd party dependency.

My question: Have you used Keychain Services in your app before? If you’ve managed to come up with any creative or interesting solutions around this framework please let me know in the comments. I’d be curious to hear about your approach. Other feedbacks, claps and shares are also welcome and appreciated.

Where to

To deepen your knowledge and understanding of this topic, I’ve collected a couple of useful resources for you. Check out these open-source projects to find out more about Keychain:

For more information about keychain: Apple’s documentation