Keychain Services in Swift

Create a generic keychain wrapper API in Swift

Bruno Lorenzo
6 min readNov 4, 2021
Photo by Maxwell Nelson on Unsplash

What would happen if someone stole a password used in your application? Does your application handle transactions (money)? Or maybe it handle user’s sensitive data, like medical information? If someone hacks into an application that you developed, any piece of information, as little as it seems, taken without your user’s concern it will be your responsibility.

There will be scenarios when this will have a higher impact than others. Nonetheless, the hacker could use any small breach to scale up and stole other assets. Imagine that a user has the same password for their bank account as well as for your application. Of course that the responsibility will be shared, but we need to take into consideration security requirements as any other feature that we develop.

We, as iOS developers, can prevent and mitigate several security risks by using the right services for the right circumstances. One of those is the Keychain Services provided by Apple. This is generally avoided by new developers because the API was written in obj-c and it could be hard to understand. However, we can create our own wrapper above Apple’s Keychain API to make the keychain interaction more “swifty friendly”.

Difference with User Defaults

First, we need to understand what is the difference between UserDefaults and the keychain. Basically, we should use UserDefaults to store users’s preferences. For example, if the user wants to remember their user name on the next login or the theme of the app (dark or light). In short terms, we store any information that doesn’t need encryption, because it’s stored in plain.

On the other hand, all the data that we store in the Keychain is encrypted by the OS. This is very convenient for storing passwords, API tokens, and any sensitive information that our application needs to handle.

Understanding the Keychain

We’ll need to use the following methods from the Keychain API:

If we take a quick look at the documentation, we’re going to notice that for all four methods, we’ll need to pass a CFDictionary as a parameter. This dictionary is a set of attributes with pre-defined keys that contains information about the type of data that will be stored, and additional information that will help us to make the search for retrieving, updating, and deleting items.

There is one key that must be present in the dictionary for all four methods: kSecClass, which refers to the type of item that we are operating with. Apple gives us five types of items to interact with the keychain:

  • Generic Password: Indicates a generic password item.
  • Internet Password: Indicates an internet password.
  • Certificate: Indicates a certificate item.
  • Class Key: Indicates a cryptographic item.
  • Class Identity: Indicates an identity item.

Depending on the type of item that we’ll be working with, the attributes that we’ll have available for usage. You can check the full list here.

Defining our own Keychain API

Let’s start by creating a new class that is going to hold our functions. In addition, let’s define a typealias to use for the attributes dictionary that we’re going to need.

From the previous section, we know that every item that we want to store in the keychain must be one of the pre-defined classes. So, for every method that our manager exposes, we’ll need to have a parameter that indicates the type of class of the item to interact with.

Each of those classes has a constant key to identify them:

  • kSecClassGenericPassword
  • kSecClassInternetPassword
  • kSecClassCertificate
  • kSecClassKey
  • kSecClassIdentity

However, as the Keychain API was written in obj-c, those keys are a CFString type. As we're trying to build a Swift wrapper, it would be a good idea to use an enum to store the different class type that the keychain supports.

We still need to use the CFString type in our functions, because we don't know the actual value of the constant. For this reason, we need to implement the RawRepresentable on our ItemClass enum.

The next step is to define some errors for our operations.

We’ll get a result of OSStatus type from all four operations. As we want to return our own errors, let’s create a helper function that convert that result type to our custom error.

We are now in a good spot to develop our API methods. For all four methods that our API will expose, there’re three parameters that we’ll need to ask for:

  • The item class that we want to operate with.
  • A key, that is going to uniquely identify, together with the class, the item in the keychain.
  • A set of optional attributes that will help to access the items faster.

Save item

  1. We encode the item to store. For that reason, the item to store must conform the Encodable protocol. Combining the usage of generics, we write only one function that can be used to store any item as long as the Encodable protocol is implemented.
  2. We create a dictionary with all the item’s information to store it. kSecClass, the item class. kSecAttrAccount, to identify the item together with the item class. kSecValueData, the actual data to store into the keychain.
  3. If we’re receiving some extra attributes, we add them to the query.
  4. We perform the keychain operation.
  5. We check the result. If it’s something different from success we convert the error to our own API errors and throw it back.

Retrieve item

As in the save, we take advantage of the generic feature to reuse the function for every type. In this case, as we are retrieving data from the keychain that we’ll decode, the generic type must be a Decodable type.

  1. We create the query just like in the save operation, but we add a new entry to tell the keychain that the data stored must be returned.
  2. We add the attributes if we receive some.
  3. Create a reference to holding the keychain result.
  4. Perform the operation.
  5. Check the result just like in the save operation.
  6. We make sure that data is present in the keychain result.
  7. We try to decode the data retrieved and send it back.

Update item

The update operation is pretty similar to the save operation. We start by encoding the data that is going to replace the one that is currently stored in the keychain. Next, we create the query dictionary to identify the item and add the extra attributes.

However, in order to update the item, we need to create another dictionary that is going to have the data to replace with.

With that in place, we call the keychain update operation and process the result like in the previous methods.

Delete Item

The delete operation is the simplest one. Like in every other method, we create our query with the item class and the key. Try to add the extra attributes, and perform the keychain delete operation. Last, we check the result and throw an error in case that the operation wasn’t successful.

Usage

Now that we have everything in place, let’s see how we can use our keychain API.

Conclusion

Working with old APIs like the keychain could be difficult, especially for beginners. We usually try to avoid it because we want the improvements that Swift gives to us. However, by applying some simple techniques, we can create our own API on top of any language API.

We should always aim to use the right features from the right scenarios, no matter how hard it could be at the start. Storing sensitive data in the keychain will increase your application’s security and will mitigate possible risks.

Have any questions? Feel free to drop me a message! 🙂

  • 🤓 Join me on Twitter for regular content on iOS development tips and insights
  • 🚀 Check out my GitHub where I share all my example projects

--

--

Bruno Lorenzo
Bruno Lorenzo

Written by Bruno Lorenzo

Software Engineer | Innovation Manager at www.houlak.com | Former iOS Tech Lead | I write about iOS, tech, and producitivy

No responses yet