Keychain Services API Tutorial for Passwords in Swift

In this Keychain tutorial for Swift on iOS, you’ll learn how to interact with the C language API to securely store passwords in the iOS Keychain. By Lorenzo Boaro.

4.7 (21) · 2 Reviews

Download materials
Save for later
Share

One of the most important security elements for Apple developers is the iOS Keychain, a specialized database for storing metadata and sensitive information. Using Keychain is the best way to store small pieces of data that are critical to your app, like secrets and passwords.

It’s complicated to interact with Keychain directly, especially in Swift. You have to use the Security framework, which is written mostly in C.

There are different Swift wrappers that allow you to interact with Keychain. Apple even offers one called GenericKeychain to make your life easier.

Although you can easily use third-party wrappers to interface with the unfriendly API that Apple provides, understanding Keychain Services adds a valuable tool to your developer tool belt.

In this tutorial, you’ll delve deep into Keychain Services API and learn how to create your own wrapper, developing it as an iOS framework.

In particular, you’ll learn how to add, modify, delete and search for both generic and internet passwords. Furthermore, you’ll provide unit tests to verify that your code works as you expect.

Getting Started

For this tutorial, you’ll use SecureStore, a boilerplate iOS framework where you’ll implement your Keychain Services API.

Start by downloading the starter project using the Download Materials button at the top or bottom of this tutorial. Once you’ve downloaded it, open SecureStore.xcodeproj in Xcode.

To keep you focused, the starter project has everything related to implementing your wrapper already set up for you.

The structure of your project should look like this:

Project Structure

The code of your wrapper lives in the SecureStore group folder:

  • SecureStoreError.swift: Contains an enum, which represents all the possible errors your wrapper can deal with. Conforming to LocalizedError, SecureStoreError provides localized messages describing the error and why it occurred.
  • SecureStoreQueryable.swift: Defines a protocol with the same name as the file. SecureStoreQueryable forces the implementer to provide a query property defined as a dictionary typed as [String: Any]. Internally, your API only deals with those types of objects. More on that later.
  • SecureStore.swift: Defines the wrapper you’ll implement in this tutorial. It provides an initializer and a bunch of stubbed methods for adding, updating, deleting and retrieving your passwords from the Keychain. A consumer can create a wrapper’s instance by injecting some type that conforms to SecureStoreQueryable.
  • InternetProtocol.swift: Represents all the possible internet protocol values you can deal with.
  • InternetAuthenticationType.swift: Describes the authentication mechanisms that your wrapper provides.
Note: Dependency injection allows you to write classes that expand and isolate functionality. It’s a bit of a scary word for a pretty simple concept. You’ll see the word “inject” throughout this tutorial, where it refers to passing a whole object into an initializer.

Along with the framework code, you should have two other folders: SecureStoreTests and TestHost. The former contains the unit tests you’ll ship with your framework. The latter contains an empty app, which you’ll use to test your framework API.

Note: Usually, to test the code you write in a tutorial, you run an app in the simulator. Instead of doing that, you’ll verify your code is working by running unit tests. So the test host app in the project won’t run in the simulator; instead, it serves as the container where it executes unit tests for your framework.

Before diving directly into the code, take a look at some theory!

An Overview of Keychain Services

Why use the Keychain over simpler solutions? Wouldn’t storing the user’s base-64 encoded password in UserDefaults be enough?

Definitely not! It’s trivial for an attacker to recover a password stored that way.

Keychain Services help you to securely store items, or small chunks of data, into an encrypted database on behalf of the user.

From Apple’s documentation, the SecKeychain class represents a database, while the SecKeychainItem class represents an item.

Keychain Services operate differently depending on the operating system you’re running.

In iOS, apps have access to a single Keychain which includes the iCloud Keychain. Locking and unlocking the device automatically locks and unlocks Keychain. This prevents unwanted accesses. Furthermore, an app is only able to access its own items or those shared with a group to which it belongs.

On the other hand, macOS supports multiple keychains. You typically rely on the user to manage these with the Keychain Access app and work implicitly with the default keychain. Additionally, you can manipulate keychains directly; for example, creating and managing a keychain that is strictly private to your app.

When you want to store a secret such as a password, you package it as a keychain item. This is an opaque type which consists of two parts: data and a set of attributes. Just before it inserts a new item, Keychain Services encrypts the data then wraps it together with its attributes.

Keychain Services

Use attributes to identify and store metadata or to control access to your stored items. Specify attributes as the keys and values of a dictionary expressed as a CFDictionary. You can find a list of the available keys at Item Attribute Keys and Values. The corresponding values can be strings, numbers, some other basic types, or constants packaged with the Security framework.

Keychain Services provide special kinds of attributes that allow you to identify the class for a specific item. In this tutorial, you’ll use both kSecClassGenericPassword and kSecClassInternetPassword to deal with generic and internet passwords.

Each class supports only a special set of attributes. In other words, not all attributes apply to a specific item class. You can verify them in the relevant item class value documentation.

Note: Besides manipulating passwords, Apple offers the chance to interact with other types of items like certificates, cryptographic keys and identities. Those are represented respectively by the kSecClassCertificate, kSecClassKey and kSecClassIdentity classes.

Diving Into Keychain Services API

Since the code hides items from ill-intentioned users, Keychain Services provide a set of C functions to interact with. Here are the APIs you’ll use to manipulate both generic and internet passwords:

  • SecItemAdd(_:_:): Use this function to add one or more items to a keychain.
  • SecItemCopyMatching(_:_:): This function returns one or more keychain items that match a search query. Additionally, it can copy attributes of specific keychain items.
  • SecItemUpdate(_:_:): This function allows you to modify items that match a search query.
  • SecItemDelete(_:): This function removes items that match a search query.

While the functions above operate with different parameters, they all return a result code expressed as an OSStatus. This is a 32-bit signed integer which can assume one of the values listed in Item Return Result Keys.

Since OSStatus could be cryptic to understand, Apple provides an additional API called SecCopyErrorMessageString(_:_:) to obtain a human-readable string corresponding to these status codes.

Note: Aside from adding, modifying, deleting or searching for a specific keychain item, Apple also provides functions to both export and import certificates, keys and identities or even modify items’ access control. If you want to learn more, check out the documentation for Keychain Items.

Now that you have a solid grasp of Keychain Services, in the next section you’ll learn how to remove the stubbed methods provided by your wrapper.