Search
Follow
Recent Comments
« Keychain group access | Main | Using categories with private methods »
Monday
Mar292010

Simple iPhone Keychain Access

The keychain is about the only place that an iPhone application can safely store data that will be preserved across a re-installation of the application. Each iPhone application gets its own set of keychain items which are backed up whenever the user backs up the device via iTunes. The backup data is encrypted as part of the backup so that it remains secure even if somebody gets access to the backup data. This makes it very attractive to store sensitive data such as passwords, license keys, etc.

The only problem is that accessing the keychain services is complicated and even the GenericKeychain example code is hard to follow. I hate to include cut and pasted code into my application, especially when I do not understand it. Instead I have gone back to basics to build up a simple iPhone keychain access example that does just what I want and not much more.

In fact all I really want to be able to do is securely store a password string for my application and be able to retrieve it a later date.

Getting Started

A couple of housekeeping items to get started:

  • Add the “Security.framework” framework to your iPhone application
  • Include the header file <Security/Security.h>

Note that the security framework is a good old fashioned C framework so no Objective-C style methods calls. Also it will only work on the device not in in the iPhone Simulator.

The Basic Search Dictionary

All of the calls to the keychain services make use of a dictionary to define the attributes of the keychain item you want to find, create, update or delete. So the first thing we will do is define a function to allocate and construct this dictionary for us:

 

static NSString *serviceName = @"com.mycompany.myAppServiceName";

- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
    NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];  
	
    [searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
	
    NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
    [searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrGeneric];
    [searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount];
    [searchDictionary setObject:serviceName forKey:(id)kSecAttrService];
	
    return searchDictionary; 
}

 

The dictionary contains three items. The first with key kSecClass defines the class of the keychain item we will be dealing with. I want to store a password in the keychain so I use the value kSecClassGenericPassword for the value.

The second item in the dictionary with key kSecAttrGeneric is what we will use to identify the keychain item. It can be any value we choose such as “Password” or “LicenseKey”, etc. To be clear this is not the actual value of the password just a label we will attach to this keychain item so we can find it later. In theory our application could store a number of passwords in the keychain so we need to have a way to identify this particular one from the others. The identifier has to be encoded before being added to the dictionary

The combination of the final two attributes kSecAttrAccount and kSecAttrService should be set to something unique for this keychain. In this example I set the service name to a static string and reuse the identifier as the account name.

You can use multiple attributes for a given class of item. Some of the other attributes that we could also use for the kSecClassGenericPassword item include an account name, description, etc. However by using just a single attribute we can simplify the rest of the code.

Searching the keychain

To find out if our password already exists in the keychain (and what the value of the password is) we use the SecItemCopyMatching function. But first we add a couple of extra items to our basic search dictionary:

 

- (NSData *)searchKeychainCopyMatching:(NSString *)identifier {
    NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
	
    // Add search attributes
    [searchDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
	
    // Add search return types
    [searchDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
	
    NSData *result = nil;
    OSStatus status = SecItemCopyMatching((CFDictionaryRef)searchDictionary,
                                          (CFTypeRef *)&result);
	
    [searchDictionary release];
    return result;
}

 

The first attribute we add to the dictionary is to limit the number of search results that get returned. We are looking for a single entry so we set the attribute kSecMatchLimit to kSecMatchLimitOne.

The next attribute determines how the result is returned. Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue. This means we will get an NSData reference back that we can access directly.

If we were storing and searching for a keychain item with multiple attributes (for example if we were storing an account name and password in the same keychain item) we would need to add the attribute kSecReturnAttributes and the result would be a dictionary of attributes.

Now with the search dictionary set up we call the SecItemCopyMatching function and if our item exists in the keychain the value of the password is returned to in the NSData block. To get the actual decoded string you could do something like:

 

    NSData *passwordData = [self searchKeychainCopyMatching:@"Password"];
    if (passwordData) {
        NSString *password = [[NSString alloc] initWithData:passwordData
                                               encoding:NSUTF8StringEncoding];
        [passwordData release];
    }

 

Creating an item in the keychain

Adding an item is almost the same as the previous examples except that we need to set the value of the password we want to store.

 

- (BOOL)createKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {
    NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];

    NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
    [dictionary setObject:passwordData forKey:(id)kSecValueData];
	
    OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);
    [dictionary release];
	
    if (status == errSecSuccess) {
        return YES;
    }
    return NO;
}

 

To set the value of the password we add the attribute kSecValueData to our search dictionary making sure we encode the string and then call SecItemAdd passing the dictionary as the first argument. If the item already exists in the keychain this will fail.

Updating a keychain item

Updating a keychain is similar to adding an item except that a separate dictionary is used to contain the attributes to be updated. Since in our case we are only updating a single attribute (the password) this is easy:

 

- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier {

    NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
    NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
    NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
    [updateDictionary setObject:passwordData forKey:(id)kSecValueData];
	
    OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,
                                    (CFDictionaryRef)updateDictionary);

    [searchDictionary release];
    [updateDictionary release];
	
    if (status == errSecSuccess) {
        return YES;
    }
    return NO;
}

 

Deleting an item from the keychain

The final (and easiest) operation is to delete an item from the keychain using the SecItemDelete function and our usual search dictionary:

 

- (void)deleteKeychainValue:(NSString *)identifier {
	
    NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
    SecItemDelete((CFDictionaryRef)searchDictionary);
    [searchDictionary release];
}

 

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (21)

Thank you very, very much!!

January 21, 2011 | Unregistered CommenterMarcel

Firstly, Thanks for a simple explanation for such complex stuff, you've done a good job here.
Secondly, I am getting status == errSecSuccess, Clearly there is some error while I am trying to add a new entry, But I can't figure out what is it.
Can you help?
I am following absolutely same steps that you explained here, so my code is hardly any different from yours here.

February 8, 2011 | Unregistered CommenterDD

Hi DD, thanks for the feedback. Keychain access code really makes my head spin :-)

I am not sure I understand where you are seeing an error? When you say you *are* getting status == errSecSuccess what makes you think it is not working? errSecSuccess is what you should expect to get when the operation completes without error.

February 8, 2011 | Registered CommenterKeith

Thanx alot for replying that fast. I was totally confused by "err"SecSuccess. I thought there was some err. It sometimes was going into errSuccess' if construct and sometimes passed it. Well, I think I understand a li'l now and am able to achieve what I was trying to do. Thanks alot and keep the good work coming. I just went through some other posts on your blog and found them really nice and helpful.

February 8, 2011 | Unregistered CommenterDD

Thank you man for good and easy to follow explanation!

February 26, 2011 | Unregistered CommenterDiejmon

I use GenericKeychain as sample to add passwords to iPhone App, but not matter how it refuse to write new item to keychain. finally I read your article. only one sentence saved me. :

"The combination of the final two attributes kSecAttrAccount and kSecAttrService should be set to something unique for this keychain."

Thank you so much!

March 9, 2011 | Unregistered CommenterHeMac

Thanks for the post, saved me a lot of time!

March 18, 2011 | Unregistered CommenterEneko Alonso

Hello! Amazing post and very easy to implement. I have a question, is this password reachable from the computer that you sync your device with? In other words, if this password is lost, is there some way of retrieving it? thank you. Best regards.

May 5, 2011 | Unregistered CommenterJuan

@Juan. Not sure I fully understand the use case - when you say the password is lost do you mean the user has forgotten it or that they deleted the app and now want to reinstall?

The keychain is included in the device backups that happen when you connect the device to iTunes. With iOS 3 the keychain backup was encrypted with a device specific key to prevent somebody with access to the backup from accessing the keys. So to recover the passwords you could restore from a backup. This meant however that if you migrated to a new device things like user passwords stored in the keychain were not restored and would need to be entered again manually.

With iOS 4, provided that you are using a passcode, the keychain is encrypted using the passcode meaning that the keychain data can be migrated to a new device. This is actually a good reason for using a passcode as it means you do not have to reenter passwords when you buy a new device and restore from backup.

However there is no way that I know of to recover the password from the encrypted backup on the host computer running iTunes if that is what you are asking unless you can brute force the encryption.

May 6, 2011 | Registered CommenterKeith

@Keith Thank you for your answer, actually what i was asking was if the user forgot the password, if there was some way of retrieving it to access the app. Like the Keychain Access app on a Mac. this is not a web based app, the password is to block access to add or delete info. Thank you.

May 9, 2011 | Unregistered CommenterJuan

@Juan - thanks now I understand what you mean. Unfortunately I don't have a good answer, the sandboxed security model in iOS devices means that there is no keychain application you can use to view password details as you can on the mac.

May 10, 2011 | Registered CommenterKeith

@Keith Thank you for your time, answers, and your amazing posts, i'll keep looking for an answer and as soon as i got one i'll post it here. Best Regards.

May 11, 2011 | Unregistered CommenterJuan

Fantastic explanation. I too was very confused with the explanation in the apple tutorial. I'm going to give this a try.

June 16, 2011 | Unregistered CommenterNathan

great help! thanks

June 17, 2011 | Unregistered Commentergormf

Thanks so much, this'll be much more secure than storing passwords in a sqlite database as i was planning. Thanks again for sharing!

July 7, 2011 | Unregistered CommenterChris

Thanks for gr8 post!!!
I want to store my Encryption/Decryption Key in iOS keychain. How to implement this?
Which attribute i have to use? kSecAttrGeneric or kSecAttrAccount ???
It would be helpful if u provide sample code....
Thanks.

July 15, 2011 | Unregistered Commenterharshit

@harshit - the example code in this post stores a username and password in the keychain so it makes use of the kSecClassGenericPassword keychain item which has the following attribute types used to identify the item being stored

- kSecAttrGeneric
- kSecAttriAccount
- kSecAttrService

You could use this same code to store an encryption key instead of the password or you could look at using the kSecClassKey keychain item. You might also want to look at the following post for some more example code:

http://useyourloaf.com/blog/2011/6/2/ios-and-keychain-migration-and-data-protection-part-3.html

July 15, 2011 | Registered CommenterKeith

Thank you for your post, I am using your methods from above to store a username and a password with this method:

- (BOOL)createKeychainValue:(NSString *)password forIdentifier:(NSString *)identifier

and I'm calling

[self createKeychainValue: @"myUsername" forIdentifier: @"username"];
[self createKeychainValue: @"mySecret" forIdentifier: @"password"];

and when I ask for username and password I get the value for password twice. I ask with
[self searchKeychainCopyMatching:@"username"];
[self searchKeychainCopyMatching:@"password"];

What is the mistake?

November 28, 2011 | Unregistered CommenterNetshark

@Netshark take a look at the following post

http://useyourloaf.com/blog/2010/4/28/keychain-duplicate-item-when-adding-password.html

I also updated the code in this post

http://useyourloaf.com/blog/2011/6/1/ios-and-keychain-migration-and-data-protection-part-2.html

Hope it helps

November 28, 2011 | Registered CommenterKeith

would it be impossible for Apple to make some realy great functions like store_secure_for_app_only(key,value) ore something instead of this completly overloaded stuff. I come from Android and I'm confused that in Android Java lost its simple nature due the limitations, but programming iPhones is simply archaic and overloaded complicated - its not only because of pointers and C - even the method names are horrible and try to do something as simple as text.trim() - .... could write a book about

January 10, 2012 | Unregistered Commenterj

Hi, thanks for this article, we would like to know how can i update ksecAttrDescrition for Item Keychain identity item ?
Because i always got the parameter error by the API :(

thank for your help!

February 12, 2012 | Unregistered Commenterios

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>