Home Articles

Mastering iOS Keychain: A Comprehensive Guide to Seamless User Data Storage and Management

Securing User Data: A Practical Guide to Keychain Implementation for Safeguarding User Data

Demo of Keychain Services iOS

Keychain Services

Securely store small chunks of data on behalf of the user.

Keychain Services iOS

Generally, we used to remember or memorize certain passwords for our Internet banking or user accounts. Sometimes, we overuse the same credentials for multiple login accounts. This is considered an insecure practice.

Keychain services API helps to solve our problem. A mechanism to store small bits of user data in an encrypted database called a keychain.

Keychain operations are Thread safe and that is guaranteed by Apple.

There is an API component we should be clear with:

SecKeychainItem

An opaque type that represents a keychain item.

keychain items — Embed confidential information in items that you store in a keychain. In simpler words, items — insert/save to the database.

kSecClassGenericPassword

We will be designing a sign-in feature with a username and password.

For Username

kSecAttrAccount — A key whose value is a string indicating the item’s account name.


    let kSecAttrAccount: CFString
    

For Password

kSecValueData — A key whose value is the item’s data.


    let kSecValueData: CFString
    

You must be wondering what is CFString!!!

CFString (Core Foundation String): is part of Apple’s Core Foundation framework, a C-based framework. It follows a reference-counted memory management model.

Saving a Keychain Item

To save an item, we can use the function.

This method will then return an OSStatus that indicates the status of the save operation.

If we get the errSecSuccess status, it means that the data has been successfully saved to the keychain.

SecItemAdd(::)



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

attributes

A dictionary that describes the item to add.

The item’s class — You use the kSecClass key to tell the Keychain services to store such as passwords, cryptographic keys and so on. Refer documentation.

kSecAttrAccount — A key whose value is a string indicating the item’s account name. In our example, we use the username.

The data. Use the kSecValueData key to indicate the data you want to store. Keychain services will take care of the encryption of the data and store it accordingly.


    func addKeychainItem(attributes attrs: CFDictionary, _ completion: @escaping (OSStatus, CFTypeRef?) -> Void) {
        queue.async {
            var item: CFTypeRef?
            let result = SecItemAdd(attrs, &item)
            completion(result, item)
        }
    }

Searching for Keychain Item To search for the keychain item, we use the following function.

Returns one or more keychain items that match a search query, or copies attributes of specific keychain items.

SecItemCopyMatching(::)


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

query

The item’s class — use the kSecClass

AttributeskSecAttrService

Search parameters — We can limit the search results to a specific number of items. Refer documentation. In this example, I will be using the kSecMatchLimitOne

Return types — to return all data and attributes for the found value.


    func findKeychainItem(attributes attrs: CFDictionaryRef, _ completion: @escaping (OSStatus, CFTypeRef?) -> Void) {
        backgroundQueue.async {
            var item: CFTypeRef?
            let result = SecItemCopyMatching(attrs, &item)
            completion(result, item)
        }
    }

Updating the existing Keychain Item

To search and update the keychain item, we use the following function. To update the keychain item, we must identify the right item. An update begins with an implicit search just like the one explicitly performed by the SecItemCopyMatching(::)

SecItemUpdate(::)


    func SecItemUpdate(
        _ query: CFDictionary,
        _ attributesToUpdate: CFDictionary
    ) -> OSStatus

query

The item’s class — You use the kSecClass

kSecAttrAccount — A key whose value is a string indicating the item’s account name. In our example, we use the username.

Search parameters to search the keychain item to update.

attributesToUpdate

The data. Use the kSecValueData key to indicate the data you want to store. Keychain services will take care of the encryption of the data and store it accordingly.


    let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
    guard status != errSecItemNotFound else { throw KeychainError.noPassword }
    guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }

Deleting the existing Keychain item

To delete the keychain item, we use the following function. This is a straightforward solution. Deleting an item from the keychain will be similar to updating the keychain item except that it only requires the query Dictionary.

SecItemDelete(_:)


    func SecItemDelete(_ query: CFDictionary) -> OSStatus

query

The item’s class — use the kSecClass

Attributes

kSecAttrAccount — A key whose value is a string indicating the item’s account name. In our example, we use the username.

Search parameters to search the keychain item to update.


    let status = SecItemDelete(query as CFDictionary)
    guard status == errSecSuccess || status == errSecItemNotFound else { throw KeychainError.unhandledError(status: status) }

The entire project can be found here

This is a free third party commenting service we are using for you, which needs you to sign in to post a comment, but the good bit is you can stay anonymous while commenting.