Basic Security in iOS 5 – Part 1

This is a post by Chris Lowe, an iOS application developer and aspiring game developer living in San Antonio, Texas. He warns that this tutorial is pretty Epic in length, but promises you’ll learn a thing or two along the way. :] One of the most important aspects of software development also happens to be […] By Chris Lowe.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Do Not Pass Alert View, Do Not Collect $200

So far so good, except there’s one small problem – it doesn’t do anything!

As you’ve probably surmised by now, we need to handle the user entering text input as well as hitting the buttons from the Alert View. Luckily, there is an easy fix to this problem – delegate methods!!

We get textFieldDidEndEditing: and alertView:didDismissButtonWithIndex: methods sent to us for FREE, and all we had to do was pay postage and shipping (and by that, I mean setting ourselves as the delegate).

Lets start with the Alert View delegate method. Add this inside ChristmasRootViewController.m:

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex 
{
    if (alertView.tag == kAlertTypePIN) {
        if (buttonIndex == 1 && self.pinValidated) { // User selected "Done"
            [self performSegueWithIdentifier:@"ChristmasTableSegue" sender:self];
            self.pinValidated = NO;
        } else { // User selected "Cancel"
            [self presentAlertViewForPassword];
        }
    } else if (alertView.tag == kAlertTypeSetup) {
        if (buttonIndex == 1 && [self credentialsValidated]) { // User selected "Done"
            [self performSegueWithIdentifier:@"ChristmasTableSegue" sender:self];
        } else { // User selected "Cancel"
            [self presentAlertViewForPassword];
        }
    }
}

Remember that enum (enumerator for you more literal types) that we created from our ChristmasConstants class? Well, now it definitely comes in handy since we can easily (as reading humans) understand which Alert View we are talking about – just the password Alert View or the alert with username/password.

So first we check which view it is. Done. Then we check to see if the user hit the “Done” button AND if what was entered is correct.

If we fail that check, we prompt the user again for their credentials because we must authenticate someone. If we pass the check, however, we let them through. We do this via a performSegueWithIdentifier: sender: method, which will kick off our Cross Dissolve modal view that we created (way) earlier.

So now we have handled the Alert View. But “Wait!” you say. “How did we get pinValidated to YES, or validate the credentials the user entered?” The latter problem is simple to solve: we check for an existing username and password that has been written to the NSUserDefaults.

Add this right above alertView:didDismissWithButtonIndex:

// Helper method to congregate the Name and PIN fields for validation.
- (BOOL)credentialsValidated 
{
    NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
    BOOL pin = [[NSUserDefaults standardUserDefaults] boolForKey:PIN_SAVED];
    if (name && pin) {
        return YES;
    } else {
        return NO;
    }
}

That takes care of the credentials check (used for first time setup), but what about pinValidated? This is where the textField delegate method comes in to save the day – and we finally get to see some security code!

Add the following method to ChristmasRootViewController.m:

#pragma mark - Text Field + Alert View Methods
- (void)textFieldDidEndEditing:(UITextField *)textField 
{
    // 1
    switch (textField.tag) {
        case kTextFieldPIN: // We go here if this is the 2nd+ time used (we've already set a PIN at Setup).
            NSLog(@"User entered PIN to validate");
            if ([textField.text length] > 0) {
                // 2
                NSUInteger fieldHash = [textField.text hash]; // Get the hash of the entered PIN, minimize contact with the real password
                // 3
                if ([KeychainWrapper compareKeychainValueForMatchingPIN:fieldHash]) { // Compare them
                    NSLog(@"** User Authenticated!!");
                    self.pinValidated = YES;
                } else {
                    NSLog(@"** Wrong Password :(");
                    self.pinValidated = NO;
                }
            }
            break;
        case kTextFieldName: // 1st part of the Setup flow.
            NSLog(@"User entered name");
            if ([textField.text length] > 0) {
                [[NSUserDefaults standardUserDefaults] setValue:textField.text forKey:USERNAME];
                [[NSUserDefaults standardUserDefaults] synchronize];
            }
            break;
        case kTextFieldPassword: // 2nd half of the Setup flow.
            NSLog(@"User entered PIN");
            if ([textField.text length] > 0) {
                NSUInteger fieldHash = [textField.text hash];
                // 4
                NSString *fieldString = [KeychainWrapper securedSHA256DigestHashForPIN:fieldHash];
                NSLog(@"** Password Hash - %@", fieldString);
                // Save PIN hash to the keychain (NEVER store the direct PIN)
                if ([KeychainWrapper createKeychainValue:fieldString forIdentifier:PIN_SAVED]) {
                    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:PIN_SAVED];
                    [[NSUserDefaults standardUserDefaults] synchronize];
                    NSLog(@"** Key saved successfully to Keychain!!");
                }                
            }
            break;
        default:
            break;
    }
}

There’s a lot to take in here, so let’s step through it together.

  1. First we use our enum again to determine which UITextField we are talking about.
  2. Get the “hash” of our password. A “hash” is simply a fixed-length number generated from another number or string (in our case, the user’s password). We’ll discuss more about why we’re using hashes in a second.
  3. Here we invoke a method called “compareKechainValeuForMatchingPIN:” from a class called KeychainWrapper. We should implement this so that you can see what it does. :] Right now, what you need to know is that it returns a BOOL of whether the value that the user entered matches what we have saved. If it does, then pinValidated is YES, else NO.
  4. We do something similar to step #3, but this time we are generating the hash ourselves and saving that to the NSUserDefaults for our future use.

After reading the above code block, you might think to yourself, “why bother with all of this hash business, and not just use the password directly?”

Storing a user’s password directly is always a bad idea. If you do so, it gives a malicious user or attacker the potential capability of retrieving the user’s raw password, which could lead to all sorts of problems for your users.

Very Important: If you learn nothing else from this tutorial, learn this: NEVER, EVER, EVER, THEN ANOTHER EVER, store the password directly. ALWAYS use some other means of hiding the user’s password, such as a hash – it still uniquely identifies what the user entered, but is not the plain value in clear text.

The same can go for the username, but it is more generally accepted that this can be less restrictive. You also want to apply this to sensitive data like credit card numbers, Social Security numbers, dates of birth – really anything that your user may not want easily exposed.

OK, so now that you know how the overall plan works, let’s implement the security code in the KeychainWrapper class so that we can see this thing in action!

Create a new file with the iOS\Cocoa Touch\Objective-C class template, enter KeychainWrapper for the Class, and make it a subclass of NSObject. Open up KeychainWrapper.h and replace it with the following:

#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import <CommonCrypto/CommonHMAC.h>

@interface KeychainWrapper : NSObject

// Generic exposed method to search the keychain for a given value. Limit one result per search.
+ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier;

// Calls searchKeychainCopyMatchingIdentifier: and converts to a string value.
+ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier;

// Simple method to compare a passed in hash value with what is stored in the keychain.
// Optionally, we could adjust this method to take in the keychain key to look up the value.
+ (BOOL)compareKeychainValueForMatchingPIN:(NSUInteger)pinHash;

// Default initializer to store a value in the keychain.  
// Associated properties are handled for you - setting Data Protection Access, Company Identifer (to uniquely identify string, etc).
+ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier;

// Updates a value in the keychain. If you try to set the value with createKeychainValue: and it already exists,
// this method is called instead to update the value in place.
+ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier;

// Delete a value in the keychain.
+ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier;

// Generates an SHA256 (much more secure than MD5) hash.
+ (NSString *)securedSHA256DigestHashForPIN:(NSUInteger)pinHash;
+ (NSString*)computeSHA256DigestForString:(NSString*)input;

@end

This class is going to do two things for us:

  • Handle read/writes to/from the keychain.
  • Manage our encryption generation methods.

Next switch to KeychainWrapper.m and add the implementation. I’m going to give you all the code at once (and there’s a lot of it!) but don’t worry – we’ll go over it in detail afterwards (plus it has a ton of comments):

#import "KeychainWrapper.h"
#import "ChristmasConstants.h"

@implementation KeychainWrapper
// *** NOTE *** This class is ARC compliant - any references to CF classes must be paired with a "__bridge" statement to 
// cast between Objective-C and Core Foundation Classes.  WWDC 2011 Video "Introduction to Automatic Reference Counting" explains this.
// *** END NOTE ***
+ (NSMutableDictionary *)setupSearchDirectoryForIdentifier:(NSString *)identifier {
    
    // Setup dictionary to access keychain.
    NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];  
    // Specify we are using a password (rather than a certificate, internet password, etc).
    [searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
    // Uniquely identify this keychain accessor.
    [searchDictionary setObject:APP_NAME forKey:(__bridge id)kSecAttrService];
	
    // Uniquely identify the account who will be accessing the keychain.
    NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
    [searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric];
    [searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount];
	
    return searchDictionary; 
}

+ (NSData *)searchKeychainCopyMatchingIdentifier:(NSString *)identifier 
{

    NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
    // Limit search results to one.
    [searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
	
    // Specify we want NSData/CFData returned.
    [searchDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
	
    // Search.
    NSData *result = nil;   
    CFTypeRef foundDict = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &foundDict);
    
    if (status == noErr) {
        result = (__bridge_transfer NSData *)foundDict;
    } else {
        result = nil;
    }
    
    return result;
}

+ (NSString *)keychainStringFromMatchingIdentifier:(NSString *)identifier 
{
   NSData *valueData = [self searchKeychainCopyMatchingIdentifier:identifier];
    if (valueData) {
        NSString *value = [[NSString alloc] initWithData:valueData
                                                   encoding:NSUTF8StringEncoding];
        return value;
    } else {
        return nil;
    }
}

+ (BOOL)createKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier 
{
   
    NSMutableDictionary *dictionary = [self setupSearchDirectoryForIdentifier:identifier];
    NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
    [dictionary setObject:valueData forKey:(__bridge id)kSecValueData];
   
    // Protect the keychain entry so it's only valid when the device is unlocked.
    [dictionary setObject:(__bridge id)kSecAttrAccessibleWhenUnlocked forKey:(__bridge id)kSecAttrAccessible];

    // Add.
    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
	
    // If the addition was successful, return. Otherwise, attempt to update existing key or quit (return NO).
    if (status == errSecSuccess) {
        return YES;
    } else if (status == errSecDuplicateItem){
        return [self updateKeychainValue:value forIdentifier:identifier];
    } else {
        return NO;
    }
}

+ (BOOL)updateKeychainValue:(NSString *)value forIdentifier:(NSString *)identifier 
{
    
    NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
    NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
    NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding];
    [updateDictionary setObject:valueData forKey:(__bridge id)kSecValueData];
	
    // Update.
    OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)searchDictionary,
                                    (__bridge CFDictionaryRef)updateDictionary);
	
    if (status == errSecSuccess) {
        return YES;
    } else {
        return NO;
    }
}

+ (void)deleteItemFromKeychainWithIdentifier:(NSString *)identifier 
{
    NSMutableDictionary *searchDictionary = [self setupSearchDirectoryForIdentifier:identifier];
    CFDictionaryRef dictionary = (__bridge CFDictionaryRef)searchDictionary;
    
    //Delete.
    SecItemDelete(dictionary);
}


+ (BOOL)compareKeychainValueForMatchingPIN:(NSUInteger)pinHash 
{
    
    if ([[self keychainStringFromMatchingIdentifier:PIN_SAVED] isEqualToString:[self securedSHA256DigestHashForPIN:pinHash]]) {
        return YES;
    } else {
        return NO;
    }    
}

// This is where most of the magic happens (the rest of it happens in computeSHA256DigestForString: method below).
// Here we are passing in the hash of the PIN that the user entered so that we can avoid manually handling the PIN itself.
// Then we are extracting the username that the user supplied during setup, so that we can add another unique element to the hash.
// From there, we mash the user name, the passed-in PIN hash, and the secret key (from ChristmasConstants.h) together to create 
// one long, unique string.
// Then we send that entire hash mashup into the SHA256 method below to create a "Digital Digest," which is considered
// a one-way encryption algorithm. "One-way" means that it can never be reverse-engineered, only brute-force attacked.
// The algorthim we are using is Hash = SHA256(Name + Salt + (Hash(PIN))). This is called "Digest Authentication."
+ (NSString *)securedSHA256DigestHashForPIN:(NSUInteger)pinHash 
{
    // 1
    NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:USERNAME];
    name = [name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    // 2
    NSString *computedHashString = [NSString stringWithFormat:@"%@%i%@", name, pinHash, SALT_HASH];
    // 3
    NSString *finalHash = [self computeSHA256DigestForString:computedHashString];
    NSLog(@"** Computed hash: %@ for SHA256 Digest: %@", computedHashString, finalHash);
    return finalHash;
}

// This is where the rest of the magic happens.
// Here we are taking in our string hash, placing that inside of a C Char Array, then parsing it through the SHA256 encryption method.
+ (NSString*)computeSHA256DigestForString:(NSString*)input 
{

    const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
    NSData *data = [NSData dataWithBytes:cstr length:input.length];
    uint8_t digest[CC_SHA256_DIGEST_LENGTH];
        
    // This is an iOS5-specific method.
    // It takes in the data, how much data, and then output format, which in this case is an int array.
    CC_SHA256(data.bytes, data.length, digest);
    
    // Setup our Objective-C output.
    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
    
    // Parse through the CC_SHA256 results (stored inside of digest[]).
    for(int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
        [output appendFormat:@"%02x", digest[i]];
    }
    
    return output;
}

@end

By the time you get to this line of text, you may be shaking or cross-eyed, or feeling uneasy about your future as a security-conscious developer!

It’s actually not that bad. Sure, it looks intimidating, but we’ll get through it in due time. For right now, let’s concentrate on the securedSHA256DigestHashForPIN method (second to last method), which will actually create the value we are going to store in the keychain.

The reason that you want to add this is to prevent what’s called a “dictionary attack,” where someone has a whole list of pre-defined passwords and goes about trying every one of them against your password field. If an attacker doesn’t know your salt hash, then they can never generate your password (provided your salt hash is long enough). To add more security, it would be better if it was generated randomly each time, but that’s another day’s tutorial!

  1. First we get the name the user has entered. This is going to be paired with other values to uniquely create our key and add some extra security to it. Then we Percent Escape the name to avoid any attempted attacks with special characters, etc. The iOS Keychain is UTF8 compliant (only), so we baseline everything to that.
  2. Next, we piece together the user’s name, the hash of the value they entered for their password, and the SALT_HASH string from our ChristmasConstants class. A “salt hash” is just an additional hash (remember, a hash is a fixed-length number based on a number or string) that can be used to augment and enhance the security of the password.
  3. Once we have our strong hash built, we pass it over to our computeSHA256DigestForString: method so that we can harden our password even more before storing it.

Now take a look at the computeSHA256DigestForString: method. It takes in a string (like the one from our securedSHA256 method) and puts it inside of a C-Style Character Array. Then it turns around and makes an NSData object of the same size in preparation of the data coming back. The digest array will actually hold our data.

Then we use a convenient CC_SHA256 method (CC being CommonCrypto, Apple’s kind-of-but-not-too-friendly cryptography framework), in which we pass in the data to be encrypted, its length, and where we want the encrypted bytes to be stored.

Once that is done, we work backwards, extract the data out of the digest array, and build a string out of it. And that is what we return.

Just a dozen lines of code (or so) is all it takes to get a pretty strongly-defended password!

One last step – go to the top of ChristmasRootViewController.m and import the KeychainWrapper class:

#import "KeychainWrapper.h"

Let’s build this bad boy one more time to see our security abilities in action!

CKStart –> CKStart2 –> CKTable1

You should be able to run it and create a username and password, and run it a second time and use the same username and password to log in. And best of all, it’s done in a secure manner!

Chris Lowe

Contributors

Chris Lowe

Author

Over 300 content creators. Join our team.