Securing User Data: A Practical Guide to Keychain Implementation for Safeguarding User Data
Securely store small chunks of data on behalf of the user.
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.
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.
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.
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.
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)
}
}
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
attributes
— kSecAttrService
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)
}
}
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
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)
}
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
Search parameters to search the keychain item to delete.
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else {
throw KeychainError.unhandledError(status: status)
}
Note
Please find the GitHub Repo
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.