Friday, April 27, 2012

Securing Data in iOS

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.

5 comments:

  1. Thanks, Steve for a great introduction to different ways of securing data. Can SQLCipher be used along with Core Data to encrypt the SQLite database?

    ReplyDelete
    Replies
    1. Rajesh,

      I haven't tried this myself, but I have seen several references where folks could not get this to work. In order for data to be encrypted/decrypted within SQLCipher, a key needs to be supplied (via a sqlite API call) and currently there is no API for that in the Core Data stack. There may be a way to do this by defining custom data store for Core Data, but again I haven't tried this or seen any examples on how this is accomplished.

      Delete
  2. Thanks Steve, is it okay to use SQLCipher in an iOS app? Will I be able to submit it on apple's ap store? Will SQLCipher be accepted by the app store, as it is an external library.

    ReplyDelete
    Replies
    1. There shouldn't be any app store issues using SQLCipher, as there are apps in the store already using it (e.g. Strip).

      The use of external libraries itself does not impact app approval. The instructions in the post build static libraries, which are an acceptable way to include external libraries in your app. If you follow the SQLCIpher instructions, there should be no issues as well.

      If you are planning to release it to non-US stores, then you will most likely need to jump through additional hoops due to the use of crypto.

      Delete
  3. This comment has been removed by a blog administrator.

    ReplyDelete