iOS KeyChain Tutorial

In iOS Keychain Tutorial, I have explained how to store data securely in Objective-C and how to share secured data between apps.

What is Keychain ?

Keychain is an encrypted container where you can store secured information like passwords,certificates,identities,…etc. In iOS, each Application has it’s own keychain. To share the data between apps, they should have the same Access Group in code signing entitlements.
iOS Keychain Tutorial

Most useful keychain API:
1. SecItemAdd – used to add an item to keychain
2. SecItemUpdate – used to modify an existing keychain item.
3. SecItemCopyMatching – used find an item in keychain and get information.
Note: To user keychain in iOS, you need to add Security.Framework

For all security API queries, we need to pass a dictionary like below:

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];

NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
[dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
[dict setObject:service forKey:(__bridge id)kSecAttrService];
[dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];

//Optiona. This is for sharing data across apps
[dict setObject:group forKey:(__bridge id)kSecAttrAccessGroup];

kSecClass -> type of item which is kSecClassGenericPassword
kSecAttrGeneric -> Generic attribute key.
kSecAttrAccount -> Account attribute key
kSecAttrService -> Name of the service.
kSecAttrAccessible -> Specifies when data is accessible.
kSecAttrAccessGroup : Keychain access group name. This should be same to share the data across apps.

How to enable sharing data across apps ?

In your project’s Target Build Settings, enable Keychain Sharing and group name as shown in the below image.

Enable Keychain  Sharing

 

This will create entitlements file in the project as shown in the below image.

Code Signin Entitlements

Content of entitlements file is:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>keychain-access-groups</key>
	<array>
		<string>$(AppIdentifierPrefix)com.apps.shared</string>
	</array>
</dict>
</plist>

Check this link for getting AppIdentifierPrefix programatically.

I have created a wrapper class named Keychain to access the keychain data Source code of: Keychain.h

#import <Foundation/Foundation.h>

@interface Keychain : NSObject
{
    NSString * service;
    NSString * group;
}
-(id) initWithService:(NSString *) service_ withGroup:(NSString*)group_;

-(BOOL) insert:(NSString *)key : (NSData *)data;
-(BOOL) update:(NSString*)key :(NSData*) data;
-(BOOL) remove: (NSString*)key;
-(NSData*) find:(NSString*)key;
@end

Source code of: Keychain.m

#import "Keychain.h"
#import <Security/Security.h>

@implementation Keychain

-(id) initWithService:(NSString *) service_ withGroup:(NSString*)group_
{
    self =[super init];
    if(self)
    {
        service = [NSString stringWithString:service_];

        if(group_)
            group = [NSString stringWithString:group_];
    }

    return  self;
}
-(NSMutableDictionary*) prepareDict:(NSString *) key
{

    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    [dict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];

    NSData *encodedKey = [key dataUsingEncoding:NSUTF8StringEncoding];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrGeneric];
    [dict setObject:encodedKey forKey:(__bridge id)kSecAttrAccount];
    [dict setObject:service forKey:(__bridge id)kSecAttrService];
    [dict setObject:(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly forKey:(__bridge id)kSecAttrAccessible];

    //This is for sharing data across apps
    if(group != nil)
        [dict setObject:group forKey:(__bridge id)kSecAttrAccessGroup];

    return  dict;

}
-(BOOL) insert:(NSString *)key : (NSData *)data
{
    NSMutableDictionary * dict =[self prepareDict:key];
    [dict setObject:data forKey:(__bridge id)kSecValueData];

    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dict, NULL);
    if(errSecSuccess != status) {
        NSLog(@"Unable add item with key =%@ error:%ld",key,status);
    }
    return (errSecSuccess == status);
}
-(NSData*) find:(NSString*)key
{
    NSMutableDictionary *dict = [self prepareDict:key];
    [dict setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
    [dict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
    CFTypeRef result = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dict,&result);

    if( status != errSecSuccess) {
        NSLog(@"Unable to fetch item for key %@ with error:%ld",key,status);
        return nil;
    }

    return (__bridge NSData *)result;
}

-(BOOL) update:(NSString*)key :(NSData*) data
{
    NSMutableDictionary * dictKey =[self prepareDict:key];

    NSMutableDictionary * dictUpdate =[[NSMutableDictionary alloc] init];
    [dictUpdate setObject:data forKey:(__bridge id)kSecValueData];

    OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)dictKey, (__bridge CFDictionaryRef)dictUpdate);
    if(errSecSuccess != status) {
        NSLog(@"Unable add update with key =%@ error:%ld",key,status);
    }
    return (errSecSuccess == status);

    return YES;

}
-(BOOL) remove: (NSString*)key
{
    NSMutableDictionary *dict = [self prepareDict:key];
    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dict);
    if( status != errSecSuccess) {
        NSLog(@"Unable to remove item for key %@ with error:%ld",key,status);
        return NO;
    }
    return  YES;
}

Add the Keychain class to your project to access keychain data

1). Initialization of the class

First define your service name and group name.

#define SERVICE_NAME @"ANY_NAME_FOR_YOU"
#define GROUP_NAME @"YOUR_APP_ID.com.apps.shared" //GROUP NAME should start with application identifier.

Keychain * keychain =[[Keychain alloc] initWithService:SERVICE_NAME withGroup:nil];

 

2). How to Add an item to keychain

NSString *key =@"YOUR_KEY";
NSData * value = [@"YOUR_DATA" dataUsingEncoding:NSUTF8StringEncoding]; 

if([keychain insert:key :value])
 {
     NSLog(@"Successfully added data");
 }
 else
  NSLog(@"Failed to  add data");

 

3). Finding an item in the keychain

NSString *key= @"YOUR_KEY"
NSData * data =[keychain find:key];
if(data == nil)
{
	NSLog(@"Keychain data not found");
}
else
{
	NSLog(@"Data is =%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}

 

4) .Update an item in the keychain

NSString *key =@"YOUR_KEY";
NSData * value = [@"NEW_VALUE" dataUsingEncoding:NSUTF8StringEncoding]; 

if([keychain update:key :value])
{
	NSLog(@"Successfully updated data");
}
else
  NSLog(@"Failed to  add data");

 

5). Remove an item from keychain

NSString *key =@"YOUR_KEY";
if([keychain remove:key])
{
    NSLog(@"Successfully removed data");
}
else
{
   NSLog(@"Unable to remove data");
}

iOS Keychain Tutorial

Reference: Apple Documentation