Securely Managing User Details in the Keychain on iOS: Part I

Share this post on:

When developing iOS applications, safeguarding sensitive user information is essential. A reliable method for securely storing data on iOS is using Keychain Services. The Keychain acts as a secure storage container, ideal for holding small pieces of sensitive information such as passwords, API tokens, or any other user-specific data. In this blog, we’ll dive into how to use Keychain in Swift to securely store user details.

What is Keychain?

Keychain Services is a secure and encrypted storage solution provided by Apple for storing sensitive information. Data stored in the Keychain is encrypted and can only be accessed by the app that created it, unless explicitly shared.

Keychain can store not only user-defined secrets like credit card information and personal notes but also crucial security components. This includes cryptographic keys and certificates required for secure communications and building trust. By using Keychain, you ensure that both explicit and important security needs are effectively managed.

Setting Up Keychain in Your Swift Project

To interact with Keychain, we will use the Security framework provided by Apple. Start by importing the Security module in your Swift file:

import Security

In the context of Apple’s Keychain Services, a query is a dictionary of attributes that defines what you want to find, update, or delete in the Keychain. The query helps the Keychain Services API locate and manipulate the specific items you’re interested in.

Here’s a detailed breakdown of the key attributes used in queries and their meanings:

Keychain Query Attributes

1 kSecClass

  • Type: CFString
  • Description: Specifies the type of Keychain item you are querying for. Common values include:
  • kSecClassGenericPassword – For generic passwords.
  • kSecClassInternetPassword – For internet passwords.
  • kSecClassCertificate – For certificates.
  • kSecClassKey – For cryptographic keys.

    Usage: Required in all queries to identify the type of item you are working with.

2 kSecAttrAccount

  • Type: CFString
  • Description: Used to specify the account associated with the Keychain item. For example, a username or email address.
  • Usage: Commonly used with kSecClassGenericPassword and kSecClassInternetPassword to uniquely identify the item within its class.


3 kSecAttrService

  • Type: CFString
  • Description: Identifies a service associated with the item. This is used in combination with other attributes to further refine your query.
  • Usage: Often used in kSecClassInternetPassword queries to specify the service for which the password is stored.

4 kSecAttrLabel

  • Type: CFString
  • Description: Provides a user-readable label for the item.
  • Usage: Used to identify items in a user-friendly way.

5 kSecAttrAccessControl

  • Type: SecAccessControl
  • Description: Specifies access control policies for the item. Determines the conditions under which the item can be accessed.
  • Usage: Used to enforce security policies like requiring device unlock before accessing the item.

6 kSecAttrAccessGroup

  • Type: SecAccessControl
  • Description: Specifies access control policies for the item. Determines the conditions under which the item can be accessed.
  • Usage: Used to enforce security policies like requiring device unlock before accessing the item.

7 kSecAttrCreationDate

  • Type: CFDate
  • Description: The date when the Keychain item was created.
  • Usage: Can be used for sorting or filtering items based on creation time.

8 kSecAttrModificationDate

  • Type: CFDate
  • Description: The date when the Keychain item was last modified.
  • Usage: Useful for tracking changes to items.

9 kSecAttrAccess

  • Type: SecAccess
  • Description: Specifies the access control settings for the Keychain item.
  • Usage: Defines who can access the item.

10 kSecValueData

  • Type: CFData
  • Description: Contains the actual data for the Keychain item, such as the password or key data.
  • Usage: Used in add or update operations to set the data for the Keychain item.

11 kSecReturnData

  • Type: CFBoolean
  • Usage: Set to kCFBooleanTrue if you want the query to return the data.
  • Description: Indicates whether the Keychain should return the data associated with the item.

12 kSecReturnRef

  • Type: CFBoolean
  • Description: Indicates whether the Keychain should return a reference to the item rather than the data.
  • Usage: Set to kCFBooleanTrue if you want the query to return a reference to the item (e.g., a SecKey).

13 kSecMatchLimit

  • Type: CFNumber
  • Description: Specifies the maximum number of matching items to return. Common values:
    1. kSecMatchLimitOne – Returns only one item.
    1. kSecMatchLimitAll – Returns all matching items.
  • Usage: Controls how many items are returned by the query.

14 kSecAttrKeyType

  • Type: CFString
  • Description: Specifies the type of cryptographic key. Common values:
  • kSecAttrKeyTypeRSA – RSA key.
  • kSecAttrKeyTypeEC – Elliptic Curve key.
  • Usage: Used with kSecClassKey to specify the type of key.

15 kSecAttrKeySizeInBits

  1. Type: CFNumber
  2. Description: Specifies the size of the key in bits.
  3. Usage: Used to define the size of the cryptographic key being generated or managed.

Example Query Usage

To retrieve a generic password from the Keychain:

import Security

func addGenericPassword(account: String, password: String) -> Bool {

    let passwordData = password.data(using: .utf8)!

    let query: [String: Any] = [

        kSecClass as String: kSecClassGenericPassword,

        kSecAttrAccount as String: account,

        kSecValueData as String: passwordData

    ]

    let status = SecItemAdd(query as CFDictionary, nil)

    if status == errSecSuccess {

        print(“Item added successfully”)

        return true

    } else {

        print(“Failed to add item: \(status)”)

        return false

    }

}

This query looks for a generic password item with the account name “myAccount” and retrieves its data if found.

Diving Into Keychain Services API:

  • SecItemAdd: Add a new Keychain item.

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

  • SecItemCopyMatching: Retrieve Keychain items based on query attributes.

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

  • SecItemDelete: Remove Keychain items.

func SecItemDelete(_ query: CFDictionary) -> OSStatus

  • SecKeyCreateRandomKey: Generate a new cryptographic key.

func SecKeyCreateRandomKey(_ parameters: CFDictionary, _ error: UnsafeMutablePointer<Unmanaged<CFError>?>?) -> SecKey?

  • SecKeyCreateEncryptedData: Encrypt data using a public key.

func SecKeyCreateEncryptedData(_ key: SecKey, _ algorithm: SecKeyAlgorithm, _ data: CFData, _ error: UnsafeMutablePointer<Unmanaged<CFError>?>?) -> CFData?

  • SecKeyCreateDecryptedData: Decrypt data using a private key.

func SecKeyCreateDecryptedData(_ key: SecKey, _ algorithm: SecKeyAlgorithm, _ data: CFData, _ error: UnsafeMutablePointer<Unmanaged<CFError>?>?) -> CFData?

  • SecItemUpdate: Update existing Keychain items.

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

The functions utilize various parameters but consistently return a result code as an OSStatus, a 32-bit signed integer representing one of the values outlined in the Item Return Result Keys.

To make these OSStatus codes more understandable, Apple provides the SecCopyErrorMessageString(_:_:) API, which translates these status codes into a human-readable format.

Image Reference from: https://developer.apple.com/documentation/security/keychain_services

Conclusion

Using Keychain in Swift provides a secure and reliable way to store sensitive user information. By encapsulating Keychain operations in a helper class, we can streamline the process and make our code more maintainable. With the steps outlined in this blog, you can easily implement secure data storage in your iOS applications, ensuring your users’ information is protected.

In the next part of this series, we will dive deeper into storing and managing user passwords and explore how to use cryptographic techniques to further enhance the security of your stored data.

For further reading and references on using Keychain and related security practices, check out these resources:

Part 2 is here! Continue learning how to securely manage user details in Keychain on iOS by diving into our latest guide.

Check it out

Happy coding!