Securing Data in iOS

by
Tags: , , , ,
Category:

There are numerous ways to secure data that you are storing on an iOS device.

The Simple/Built-in Way

The simplest way is to take advantage of the iOS Data Protection (iOS 4+). This can be accomplished by setting an attribute on a file like this:

    [[NSFileManager defaultManager] createFileAtPath:[self filePath]
                    contents:[@"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding]
                    attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete
                                                           forKey:NSFileProtectionKey]];

There are several different levels of file protection. The following was taken from the NSFileManager class reference from Apple:

  • NSFileProtectionNone – no file protection
  • NSFileProtectionComplete – file is encrypted when the device is locked or booting
  • NSFileProtectionCompleteUnlessOpen – file is encrypted and can only be opened when the device is unlocked. Once open, the file can continue to accessed even if the user locks the device.
  • NSFileProtectionCompleteUntilFirstUserAuthentication – The file is stored in an encrypted format on disk and cannot be accessed until after the device has booted. After the user unlocks the device for the first time, your application can access the file and continue to access it even if the user subsequently locks the device.

A Core Data sqlite store can also be encrypted by setting the NSFileProtectionKey file attribute to one of the above values (after you create your persistent store coordinator).

However, an important thing to realize is that this type of data protection requires the device to have a passcode set on it (http://support.apple.com/kb/HT4175).

CommonCrypto

What if you really need to insure that your data is protected regardless of whether the device has a passcode set? One way is to use the CommonCrypto libraries from Apple. This can be fairly complex. There is a great write up here from Rob Napier on what’s involved. Fortunately, he has also provided a wrapper that greatly simplifies this process here. Using this library (or writing your own), you can encrypt your data and store it wherever you need. If you are using Core Data, you could write an NSValueTransformer for the Core Data entity attributes that require encryption using CommonCrypto or the RNCrypto library to encrypt/decrypt the attribute values.

SQLCipher

One more way to protect your data, specifically data that you want to store in a SQLite database, would be to use SQLCipher. SQLCipher encrypts/decrypts data at the page level and is transparent to your application code. You still use the standard SQLite APIs, with one additional method call when accessing the database (passing your key to sqlite). There are excellent instructions on setting it up for use in an iOS project here.

You can build SQLCipher as a set of static libraries by following the same iOS instructions with some modifications and additions. Here are the high level steps:

  1. Instead of creating a view based iOS project, create a static library project
  2. Follow the balance of the steps for including SQLCipher in your project
  3. Add a new “Aggregate” build target
  4. Add a “Run Script” build phase and paste the following script into it. This will build both the simulator and iOS based targets for the libcrypto, libsqlcipher, and libssl libraries.
  5. xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphonesimulator -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator
    xcodebuild -project ${PROJECT_NAME}.xcodeproj -sdk iphoneos -target ${PROJECT_NAME} -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos
    
  6. Add another “Run Script” build phase and paste the following script into it. This will merge the simulator and iOS builds into one for each library. These three libraries are what would be added to a project using SQLCipher.
  7. CRYPTO_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libcrypto.a" &&
    SQLCIPHER_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libsqlcipher.a" &&
    SSL_LIB="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/libssl.a" &&
    DEVICE_CRYPTO_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libcrypto.a" &&
    DEVICE_SQLCIPHER_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libsqlcipher.a" &&
    DEVICE_SSL_LIB="${BUILD_DIR}/${CONFIGURATION}-iphoneos/libssl.a" &&
    UNIVERSAL_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal" &&
    # Create framework directory structure.
    rm -rf "${UNIVERSAL_LIBRARY_DIR}" && mkdir -p "${UNIVERSAL_LIBRARY_DIR}" &&

    # Generate universal binary for the device and simulator. lipo "${CRYPTO_LIB}" "${DEVICE_CRYPTO_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libcrypto" && lipo "${SQLCIPHER_LIB}" "${DEVICE_SQLCIPHER_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libsqlcipher" && lipo "${SSL_LIB}" "${DEVICE_SSL_LIB}" -create -output "${UNIVERSAL_LIBRARY_DIR}/libssl"

Once completed, you can build your new aggregate target and there should be 3 libraries located in a subdirectory within your project build directory. Just add these to your XCode project as you would any other library and you are good to go.