Simplify Your Keychain Access

Understanding how to work with and leverage the power of Keychain Services is an essential part of iOS development. The SecItem API’s comes as part of the Security framework in iOS and allows you to save sensitive user information inside the Secure Enclave built into all iOS devices. In case you need to be in the know, the Secure Enclave is defined by Apple as:

The Secure Enclave […] provides all cryptographic operations for Data Protection key management and maintains the integrity of Data Protection even if the kernel has been compromised.

[…]

Each Secure Enclave is provisioned during fabrication with its own Unique ID that is not accessible to other parts of the system and is not known to Apple. When the device starts up, an ephemeral key is created, entangled with its UID, and used to encrypt the Secure Enclave’s portion of the device’s memory space.

Additionally, data that is saved to the file system by the Secure Enclave is encrypted with a key entangled with the UID and an anti-replay counter.

The Secure Enclave is responsible for processing fingerprint data from the Touch ID sensor, determining if there is a match against registered fingerprints, and then enabling access or purchases on behalf of the user. Communication between the processor and the Touch ID sensor takes place over a serial peripheral interface bus. The processor forwards the data to the Secure Enclave but cannot read it. It’s encrypted and authenticated with a session key that is negotiated using the device’s shared key that is provisioned for the Touch ID sensor and the Secure Enclave. The session key exchange uses AES key wrapping with both sides providing a random key that establishes the session key and uses AES-CCM transport encryption.

There’s a lot of big words in this excerpt, but understand this: when it comes to storing data, the Secure Enclave is a the most secure place in the system. It is designed from the ground up to be a black box that can only be accessed by known parties with the right keys, keys that would only work for one instance of the keychain (the one on your device).

On a broader picture, this bit of technology is what allows Apple to spearhead the competition in terms of technology, and what has of late caused a lot stir in the media after the FBI obtained a court ruling against Apple  in part of the latest developments in the  Encryption Wars.

Now, getting back to the point, how can we, as developers, make use of this technology to ensure the security of our customers’ data?

First is understanding when we need to use this. What data should we consider sensitive? A non-exhaustive list would probably include:

  • passwords
  • credit card information
  • health data
  • financial data
  • personal identity data
  • access tokens
  • cryptography keys
  • security keys

There’s a very good WWDC video that provides you with a few common solutions for working with the keychain, from which this tutorial is built from. So if you haven’t seen it I highly suggest you take a look at it first. There’s also a few things to consider before we jump into the code:

  • The SecItem (keychain) API’s is a low level C-based framework that you can access and edit making use of attribute dictionaries in combination with specific functions. Some of its solutions are therefore obscure and hard to remember, albeit them being very methodical.
  • Utilities are wrapped into very idiosyncratic and process driven logics for creating, editing and deleting items, where you cannot create or delete items twice (doing so will throw errors in your program).
  • By creating a wrapper geared towards common uses we can greatly simplify our logic for Keychain Access and make it more developer-safe.
  • The types of items that you can store in the keychain are grouped into the following categories:
    1. Generic passwords
    2. Internet passwords (for use with Safari)
    3. Certificates
    4. Keys (application support keys)
    5. Identities, or Certificates + Private Keys combinations.
  • For reasons of simplicity, we will focus this tutorial exclusively on Generic Passwords, other categories will make use of similar functional logic, but will most probably require a different set of keys in their attributes dictionary.

Alright then, we’re ready now, lets jump straight to it ->

Handling Generic Passwords

From outside our low level wrapper, we really want to have the most simple interface possible. We want to have the ability to:

  • Check if a password exists in our keychain
  • Fetch a stored password
  • Save a password
  • Remove a password

These utilities will constitute our four main public class functions in our keychain wrapper class (there’s really no need to have instances because we aren’t doing anything that would require the use of stored properties). We will define our functions, for now, as:

final class EPICKeychainManager {

  class func storePassword(password:String, forKey key:String {
    //TODO
  }

  class func checkPassword(password:String, forKey key:String) -> Bool {
    //TODO
    return false
  }

  class func passwordForKey(key:String) -> String? {
    //TODO
    return nil
  }

  class func removePasswordForKey(key:String) -> Bool {
    //TODO
    return false
  }
}

So now lets go through them one by one:

Fetching passwords

The code for fetching passwords is as follows:

class func passwordForKey(key:String) -> String? {
  var attributes = baseDictionaryForKey(key)
  attributes[String(kSecReturnData)] = NSNumber(bool: true)
  var data : AnyObject?
  if SecItemCopyMatching(attributes, &data) == errSecSuccess {
    if let data = data as? NSData {
      return String(data: data, encoding: NSUTF8StringEncoding)
    }
  }
  return nil
}

private class func baseDictionaryForKey(key:String) -> [String: AnyObject] {
  var attributes = [String: AnyObject]()
  attributes[String(kSecClass)] = String(kSecClassGenericPassword)
  attributes[String(kSecAttrService)] = "EPICKeychainManager"
  attributes[String(kSecAttrAccount)] = key
  return attributes
}

Note that we’ve created a private class function called baseDictionaryForKey:. The reason for this is that all keychain functions we will be using will require the use of a base dictionary of attributes containing a reference for the service (i.e: your app name) they key for the password (i.e: account name, userId, other) and the security class (in our case Generic Passwords). I’ve grouped this into a separate function in order to avoid duplicating code unnecessarily as part of an optimisation effort.

Our main function, on the other hand, is quite straightforward. We fetch a mutable copy our base dictionary and append to it the kSecReturnData key, which lets the keychain know that we want to retrieve the password data from it. We then fetch the data using the SecItemCopyMatching() function. If we get any data back, we can return the String equivalent for this data (assuming it was previously stored as a String). If our function returns nil, this means that the keychain does not have a password stored for the key + service attributes specified by us.

Checking passwords

Once we have the ability for fetching passwords in place, checking for the existence of passwords becomes a simple matter of checking against the return value of our passwordForKey() function:

class func checkPassword(password:String, forKey key:String) -> Bool {
  if let storedPassword = passwordForKey(key) {
    return storedPassword == password
  }
  return false
}

Deleting passwords

At times in the course of your program you might want to delete previously stored items in the keychain (token expiries, user log outs), this can be achieved with the following code, building up from our previous definitions:

class func removePasswordForKey(key:String) -> Bool {
  if checkIfKeyExists(key) {
    let attributes = baseDictionaryForKey(key)
    return SecItemDelete(attributes) == errSecSuccess
  }
  return true
}

private class func checkIfKeyExists(key:String) -> Bool {
  if let _ = passwordForKey(key) {
    return true
  }
  return false
}

We check to see if an item already exists in the Keychain, if so, we delete the item by passing our base attributes to the SecItemDelete() function, if not we can assume the key has been deleted and we return true.

Setting passwords

As mentioned before, the Keychain is very strict upon enforcing a differentiation between creating and updating passwords, but from a developers perspective this can be a bit inconvenient. What we would really like is a single entry method, that creates or updates a password value as and when needed. In order to accomplish this we will use the following set of functions:

class func storePassword(password:String, forKey key:String) -> Bool {
  if checkIfKeyExists(key) {
    return updatePassword(password, forKey: key)
  }
  return createNewPassword(password, forKey: key)
}

private class func createNewPassword(password:String, forKey key:String) -> Bool {
  let data = password.dataUsingEncoding(NSUTF8StringEncoding)
  var attributes = baseDictionaryForKey(key)
  attributes[String(kSecValueData)] = data
  return SecItemAdd(attributes, nil) == errSecSuccess
}

private class func updatePassword(password:String, forKey key:String) -> Bool {
  let attributes = baseDictionaryForKey(key)
  let data = password.dataUsingEncoding(NSUTF8StringEncoding)
  var changes = [String: AnyObject]()
  changes[String(kSecValueData)] = data
  return SecItemUpdate(attributes, changes) == errSecSuccess
}

Depending on the return value of our function checkIfKeyExists(), we will use a separate logic for updating and creating new items in the keychain. Both functions start with a similar logic, we fetch our base keychain attributes and append the data to the kSecValueData key. The difference is in what Security function we call to update [ SecItemUpdate(changes:) ] or add [ SecItemAdd() ].


Well then, I hope this new tutorial has proven useful for you all! As always, feel free to download the example code for this tutorial from my GitHub account.

All the best!

Author: Danny Bravo

Director @ EPIC