Creating a Simple Keychain Wrapper

      1 Comment on Creating a Simple Keychain Wrapper

In this short blog post, we’ll cover some basics on how to use the keychain on iOS, and we’ll create a simple wrapper for the keychain.

You’ve probably heard of an iOS keychain. If not, it’s a secure persistent storage, meant to be used to store sensitive information, like passwords, tokens, credit card numbers, etc. Keychain API is pretty simple, it only has 4 main methods that you’ll use, but this can be abstracted away, and it can be made much simpler. Most of you won’t need the advanced features of the keychain, and you’ll just want to save and load your passwords/tokens reliably. In this article, we’ll see how to create a keychain wrapper that behaves like a dictionary.

Keychain Basics

Each iOS app has one keychain, and it can only access its own keychain. You can share keychains between apps, but only if the apps are from the same publisher (meaning, they’re your own apps). If you want to share keychains across your apps, when you enable ‘Keychain Sharing’ in your capabilities tab make sure all your apps use the same keychain group, like shown on the image below:

Let’s get some terminology out of the way. Your keychain has keychain items, and each item has attributes. When you set your keychain item, you select the item class, and based on this class you’ll have different attributes available. We see this on a simple diagram:

You have only 4 methods to interact with your keychain:

  • SecItemCopyMatching
  • SecItemAdd
  • SecItemUpdate
  • SecItemDelete

The names of the methods are pretty self-explanatory, except the first one. ‘SecItemCopyMatching’ is basically just a query method that enables you to search for items in your keychain.

For every one of these 4 operations, you’ll need to use a query. A query is simply a dictionary that the Keychain Framework will use to find the item you’re interested in. In the dictionary, you’ll specify your class type, based on that type you’ll be able to specify some attributes. You can find a comprehensive list of item attributes on apples’ developer portal.

If you don’t specify correct data for this dictionary you’ll get ‘errSecParam’, which is probably the most common error you’ll see when working with the keychain. Just make sure you’re using the proper items for your item class and you’ll be grand.

The code

Now that we have a general idea of how things are supposed to work, let’s create our wrapper. The wrapper that we’ll create will be used just like a dictionary, here’s an example of how it’ll be used when we’re finished:

let keychain = DAKeychain()
        
// Store
keychain["key"] = "confidential data"
        
// Fetch
let value = keychain["key"]

// Delete
keychain["key"] = nil

That’s a pretty convenient way of using a keychain so safely store confidential data, wouldn’t you say.

The first thing we’ll need is the dictionary that we’ll use to query the keychain:

    private func keychainQuery(withKey key: String) -> NSMutableDictionary {
        let result = NSMutableDictionary()
        result.setValue(kSecClassGenericPassword, forKey: kSecClass as String)
        result.setValue(key, forKey: kSecAttrService as String)
        result.setValue(kSecAttrAccessibleAlwaysThisDeviceOnly, forKey: kSecAttrAccessible as String)
        return result
    }

As you can see we’re setting our item class to ‘kSecClassGenericPassword’ and we’re using ‘kSecAttrService’ attribute for our key. ‘kSecAttrAccessible’ is used to tell the keychain when our app needs access to it. We’re just using the least restrictive option here.

We’ll be using this dictionary to save and load data into the keychain, let’s examine the load method first:

    private func load(withKey key: String) -> String? {
        let query = keychainQuery(withKey: key)
        query.setValue(kCFBooleanTrue, forKey: kSecReturnData as String)
        query.setValue(kCFBooleanTrue, forKey: kSecReturnAttributes as String)
        
        var result: CFTypeRef?
        let status = SecItemCopyMatching(query, &result)
        
        guard
            let resultsDict = result as? NSDictionary,
            let resultsData = resultsDict.value(forKey: kSecValueData as String) as? Data,
            status == noErr
            else {
                logPrint("Load status: ", status)
                return nil
        }
        return String(data: resultsData, encoding: .utf8)
    }

We create our query dictionary and set two additional values on it. ‘kSecReturnData’ will return the data stored in the dictionary, and ‘kSecReturnAttributes’ will return the dictionary with attributes (along with the data). If we wanted, we could set this value to false, in that case, we would get a data back from the query instead of a dictionary.

Once we perform the query on the keychain we cast the result to a dictionary, and fetch data from the dictionary using ‘kSecValueData’ key. At the end of the function, we simply return the string with UTF8 data.

Now we can fetch data from the keychain, let’s see how to save/update it:

    private func save(string: String?, forKey key: String) {
        let query = keychainQuery(withKey: key)        
        let objectData: Data? = string?.data(using: .utf8, allowLossyConversion: false)

        if SecItemCopyMatching(query, nil) == noErr {
            if let dictData = objectData {
                let status = SecItemUpdate(query, NSDictionary(dictionary: [kSecValueData: dictData]))
                logPrint("Update status: ", status)
            } else {
                let status = SecItemDelete(query)
                logPrint("Delete status: ", status)
            }
        } else {
            if let dictData = objectData {
                query.setValue(dictData, forKey: kSecValueData as String)
                let status = SecItemAdd(query, nil)
                logPrint("Update status: ", status)
            }
        }
    }

In the save method we’ll be saving a string for a key. After creating our query dictionary we’ll convert our string to data. Next, we search the keychain with ‘SecItemCopyMatching’ and if we get a ‘noErr’ result it means we have a value in the keychain.

We check if the data we’re trying to save is nil or not. If it’s not nil, we create a dictionary with our data and a ‘kSecValueData’ key (we used this key to fetch data in the load method, remember). We call ‘SecItemUpdate’ with the original query and the new dictionary and the data is saved.

If the data we’re trying to save is nil, we’ll delete an item for the key from the dictionary. This is equivalent to how a dictionary works. To delete an item we call ‘SecItemDelete’ and that’s it.

If we didn’t find the item by using ‘SecItemCopyMatching’ it means we’ll have to add a new item to the keychain. We update the original query and set the string data representation for the ‘kSecValueData’ key and call ‘SecItemAdd’.

It’s as simple as that, in the next section we’ll add a bit of sugar and use subscripting, in order to make our keychain wrapper really simple to use.

Subscripting

If you don’t know what subscripting is, it’s basically a convenient way of getting items from a collection. For example, if you want to fetch an item from an array at index 5 you would fetch it like this:

let value = array[5]

Those angled brackets are called subscripts. And you can implement your own really easily, so let’s see how to do it:

    subscript(key: String) -> String? {
        get {
            return load(withKey: key)
        } set {
            save(string: newValue, forKey: key)
        }
    }

Now it should be perfectly clear. Subscript behaves like a simple property where we implement custom get and set methods. The value within the angled brackets is called ‘key’ and we’re specifying that our key is of type ‘String’, we also specify that our return type is an optional string because we might not have a value for the key we’re looking for. In our case, when we try to get a value, we use the load function with the passed in key, and when we try to set a value, we use the save function. The new value is passed to us in the set function, the variable name is ‘newValue’. This should all look very familiar to you if you used custom getters and setters for your properties.

Conclusion

I know that in time you’ll want to save some sensitive data, and you’ll want to do it in a simple manner in your code, without cluttering your classes. This wrapper class should enable you to do so. It’s real simple to use, and it hides all of the boilerplate code.

This was a short post, for a change, and I hope it was helpful to you. All the code can be found on my GitHub account.

As always, have a nice day 🙂

Dejan.

More resources

One thought on “Creating a Simple Keychain Wrapper

  1. Pingback: Creating a Simple Keychain Wrapper – Home

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.