Basic Security in iOS 5 – Part 2
This is a post by Chris Lowe, an iOS application developer and aspiring game developer living in San Antonio, Texas. Welcome to Part Two of the epic tutorial series on implementing basic security in iOS 5! In Part One, we began work on Christmas Keeper, an app designed to keep track of holiday gift ideas. […] By Chris Lowe.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Basic Security in iOS 5 – Part 2
40 mins
This is a post by Chris Lowe, an iOS application developer and aspiring game developer living in San Antonio, Texas.
Welcome to Part Two of the epic tutorial series on implementing basic security in iOS 5!
In Part One, we began work on Christmas Keeper, an app designed to keep track of holiday gift ideas. We built out the shell for our app and put it under lock and key via prompting the user for a PIN/password. In the process, we learned a little about hashing and cryptography.
In this second and final part of the series, we’ll tackle using the iOS Keychain and Data Protection API, as well as several more segues and the new Twitter API.
This tutorial starts where the previous tutorial left off, so if you don’t have it already, grab the project where we left off last time.
Read on to get even more security-savvy!
Keys to the Kingdom
Before we jump back into the app, let’s take a deeper look at the methods in the KeychainWrapper class. These methods are all helper methods to help you manage items in your keychain.
The keychain in iOS (and Mac) is a highly secure location inside of the OS that is the de facto storage location for user names, passwords, logon tokens, secret keys, etc. It’s a feature provided to you out of the box, with the additional “benefit” of being C-based (so it’s faster).
This is the ideal place to store our sensitive data for several additional reasons:
- The keychain is located OUTSIDE of your application, you just have access to it (via the provided API). This is important should your application ever be compromised (read: jailbroken/rooted) and someone has access to all of the information in your binary.
- The keychain is encrypted and managed for you – going the route of saving to a PLIST, Core Data database, etc. saves files in plaintext so you’ll need to manage the encryption of it yourself (not a trivial task). Plus, that’s where Apple saves their data too, seems like a safe bet to me :).
- The keychain can be migrated for you automatically across backup/restores so the user can pick up from where they left off.
That being said, the keychain is an I/O monster and is not meant for large amounts of data – if you have many sensitive data elements, you might need to consider something like a Core Data database and roll your own encryption, unfortunately.
Also, just because the password is hashed and more secure, does not mean we are free to post it to Twitter for all to see! It’s still sensitive information and, again, given enough time and computational horsepower, can be broken so it’s best to store it somewhere to safer.
Likewise, each app has its space in the keychain, and this keychain access can be shared across apps (typically within the same company, not cross-company). As you’ll see in the code, jumping from Objective-C to Core Foundations (C code) while using ARC makes for some interesting keywords that are easy to miss and misuse.
We’re going to open up KeychainWrapper.m, and take a look at each method but before we do that, let’s review the Dictionary keys we are going to use/can use to setup our Keychain entry. For more technical details on the iOS Keychain implementation, Apple has a great reference document on Keychain Services for iOS (including their own version of reading/writing to the keychain). Be sure to brush up on “Keychain Services Concepts” there as well because “knowing is half the battle!”
In general, the most common keys you are going to set in the Keychain are below (take from Apple’s sample code at the above link).
[keychainData setObject:@"Item label" forKey:(id)kSecAttrLabel]; // Not used
[keychainData setObject:@"Item description" forKey:(id)kSecAttrDescription]; // Not used
[keychainData setObject:@"Service" forKey:(id)kSecAttrService];
[keychainData setObject:@"Account" forKey:(id)kSecAttrAccount];
[keychainData setObject:@"Your comment here." forKey:(id)kSecAttrComment]; // Not used
[keychainData setObject:@"password" forKey:(id)kSecValueData];
// The below are taken from another methods so they don't use "keychainData" but still function the same
[genericPasswordQuery setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
[genericPasswordQuery setObject:keychainItemID forKey:(id)kSecAttrGeneric];
[genericPasswordQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
Here is how to read the keys: “k” is an Apple (perhaps Industry) standard for a Constant (usually defined in an enum). “Sec” means Security. “Attr” means Attribute. The remainder is what the attribute is, for example “Label” is the Label for the Keychain entry.
Some of these keys are just carry over from the Mac such as kSecAttrLabel, kSecAttrDescription, and kSecAttrComment. These are important on a Mac because users can see the keychain using the Keychain Access app and you want your Keychain entry to provide enough information that the user understands what it’s for and doesn’t accidentally delete it. However on iOS, Apple has deemed viewing the Keychain a unnecessary for users. The keys we do use in the app however, include:
[searchDictionary setObject:APP_NAME forKey:(__bridge id)kSecAttrService];
This key uniquely identifies the “Service name” for our key entry (remember, the Keychain is a shared repository). In this instance, we are just using our App Name (ChristmasKeeper, from our Constants.h class) which is pretty unique but another popular option is your company name in reverse-DNS notation (com.raywenderlich.iOSSecurityFTW).
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric];
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount];
These two lines of code specify WHO will be accessing our keychain. We are using the same identifier for both to keep things simple, but you can specify different values for each.
[dictionary setObject:valueData forKey:(__bridge id)kSecValueData];
This is the actual data that we are saving which, in our case, is the hashed password from the user.
[searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
Here we are specifying that we are storing a Password which is the most common type of entry for iOS. You can also store “Internet Passwords” and certain Certificates but this is mostly for Mac usage.
[searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
Technically, we should only get one result back from our search – if we get more than one back we either didn’t specify a specific enough search and/or our keychain entry wasn’t unique enough. In any event, we only want one back, so we specify that here.
[searchDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
This last one is a bit hard to read, but what it is saying is that when we do a search for a keychain value, we are going to get something back and we have the opportunity to specify what that return type is. In this case, we want “Data” back, which just happens to be CFData which we later change into the familiar NSData.
Now let’s look at each method individually.
You’ll notice the “(__bridge id)” used quite a bit in conjunction with this method. This is an ARC directive that specifies a type-cast (in this case to identify) for the respective CF type (all of the kSecAttr options are of type CFTypeRef). One alternative to this code is to turn off ARC for this class, but that’s the easy way out. :]
One important line of code is:
This is what actually searches the keychain. It takes in search parameters (via searchDictionary) and an output parameter (foundDict).
Like the JSON file below, we want to protect this key from being accessed while the device is locked, so we set the following line of code:
This limits when the key can be accessed. We also check to see if the key already exists. If it does, we update it instead of creating a new one or deleting the old one.
-
+ (NSMutableDictionary *)setupSearchDirectoryForIdentifier:(NSString *)identifier
You can think of this method as the foundation for the rest of the keychain helper methods. This method sets up all of the default parameters needed to easily reach the keychain.You’ll notice the “(__bridge id)” used quite a bit in conjunction with this method. This is an ARC directive that specifies a type-cast (in this case to identify) for the respective CF type (all of the kSecAttr options are of type CFTypeRef). One alternative to this code is to turn off ARC for this class, but that’s the easy way out. :]
-
+ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier
I’d call this method the “raw data” method. It searches the keychain for the value we’re requesting, but it returns it as an NSData object. We then have to manually manipulate it ourselves.One important line of code is:
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &foundDict);
This is what actually searches the keychain. It takes in search parameters (via searchDictionary) and an output parameter (foundDict).
-
+ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier
This is really the method you want to use for searching the keychain, because it returns the value found as an NSString, which is much easier to compare against another input value (such as the password entered by the user). -
+ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier
This method writes to the keychain. We use “SecItemAdd,” which is the key line of code that physically writes to the keychain.Like the JSON file below, we want to protect this key from being accessed while the device is locked, so we set the following line of code:
[dictionary setObject:(__bridge id)kSecAttrAccessibleWhenUnlocked forKey:(__bridge id)kSecAttrAccessible];
This limits when the key can be accessed. We also check to see if the key already exists. If it does, we update it instead of creating a new one or deleting the old one.
-
+ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier
Like createKeychainValue, but updates a value that is already in the keychain. -
+ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier
A simple method to delete a keychain value. -
+ (BOOL)compareKeychainValueForMatchingPIN:(NSUInteger)pinHash
A convenient method that compares the input value (in SHA256 encrypted format) to the value in the keychain.
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &foundDict);
[dictionary setObject:(__bridge id)kSecAttrAccessibleWhenUnlocked forKey:(__bridge id)kSecAttrAccessible];
Remember, we used these methods in the last tutorial in ChristmasRootViewController.m’s textFieldDidEndEditing method. Now that you understand how the keychain methods work a bit better, walk through how the code and make sure you fully understand how it works now.