FMDB with SQLCipher Tutorial

来源:互联网 发布:知乎发帖 编辑:程序博客网 时间:2024/05/16 17:52
After long hours of suffering, searching for a simple example on how to get my current SQLite database and encrypt using SQLCipher and FMDB, I was able to work on a code that is simple and it works!

The code below shows how you can get a current SQLite database and encrypt using fmdb + sqlcipher. You can use a database with empty tables, an empty database and even a working database with table and data already stored in them. Please try the solution below and tell me in the comments if it worked for you or if you had any problems with it.

Getting Started

First thing you’ll need and this is very important, is to use Cocoapods to load the correct libraries and configuration. By using Cocoapods, it will install FMDB + SQLCipher in seconds for you.
If you don’t know what Cocoapods is (???), I’ve been meaning to write a simple tutorial, but while that does not happen, check this link

Open your Podfile and add the following line

pod 'FMDB/SQLCipher'

If you already had FMDB in your Podfile, remove the line. This pod already have the FMDB dependency and it will install FMDB automatically

Now run pod update (or pod install if first time installing the pods for your project) in the terminal to install the new pods.
After the pods are installed, open your .xcworkspace

Creating a Copy of the Database File in the Documents Folder

If you already know how to do this or are already doing, you can skip this part as this does not change from the default behavior

In my game, I already had a default database with all the tables that I needed inside my project Resources folder, once I run the app I check to see if the database is in the Documents folder, if it’s not, I copy the file from the Resources folder to the Documents folder.

In the AppDelegate I have a NSString property called databasePath which I use throughout the application and in application:didFinishLaunchingWithOptions method I set this property with my default database path by doing:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{...    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);    NSString *documentDir = [documentPaths objectAtIndex:0];    self.databasePath = [documentDir stringByAppendingPathComponent:@"gameDefault.sqlite"];        [self createAndCheckDatabase];...}

You’ll notice that I also call a separate method createAndCheckDatabase which actually do the verification I told above and create a copy of the database file in case it does not exist in the Documents folder.

-(void) createAndCheckDatabase{    BOOL success;        NSFileManager *fileManager = [NSFileManager defaultManager];    success = [fileManager fileExistsAtPath:self.databasePath];        if(success) return; // If file exists, don't do anything    // if file does not exist, make a copy of the one in the Resources folder    NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"gameDefault.sqlite"]; // File path       [fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil]; // Make a copy of the file in the Documents folder}

After you’ve done this, you’ll have your database in the Documents folder where it will be accessible to the application to read/write.

Working the magic of SQLCipher

So, in order for you to have an encrypted database, you’ll need to create a new blank encrypted database and then ATTACH you existing database to it. By attaching, you will have everything that you had in your unencrypted database in the new encrypted one.

Let’s start by importing SQLite to your AppDelegate.m file. At the top of you file add

#import <sqlite3.h>

Now inside of the createAndCheckDatabase method, add the following below what we already have

    // Set the new encrypted database path to be in the Documents Folder    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);    NSString *documentDir = [documentPaths objectAtIndex:0];    NSString *ecDB = [documentDir stringByAppendingPathComponent:@"encrypted.sqlite"];    // SQL Query. NOTE THAT DATABASE IS THE FULL PATH NOT ONLY THE NAME    const char* sqlQ = [[NSString stringWithFormat:@"ATTACH DATABASE '%@' AS encrypted KEY 'secretKey';",ecDB] UTF8String];        sqlite3 *unencrypted_DB;        if (sqlite3_open([self.databasePath UTF8String], &unencrypted_DB) == SQLITE_OK) {        // Attach empty encrypted database to unencrypted database        sqlite3_exec(unencrypted_DB, sqlQ, NULL, NULL, NULL);                // export database        sqlite3_exec(unencrypted_DB, "SELECT sqlcipher_export('encrypted');", NULL, NULL, NULL);                // Detach encrypted database        sqlite3_exec(unencrypted_DB, "DETACH DATABASE encrypted;", NULL, NULL, NULL);                sqlite3_close(unencrypted_DB);    }    else {        sqlite3_close(unencrypted_DB);        NSAssert1(NO, @"Failed to open database with message '%s'.", sqlite3_errmsg(unencrypted_DB));    }        self.databasePath = [documentDir stringByAppendingPathComponent:@"encrypted.sqlite"];

Breaking down this code

  1. In the first 3 lines we get the Documents Directory path and set the NSString ecDB to that path with the new database name.
  2. Then we create the SQL Query we’re going to execute first to do the ATTACH. Note that we set 2 parameters in there, the DATABASE and the KEY. The DATABASE should be the full path to the encrypted database you want to create, in this case, string ecDB, and the KEY parameter is the key that’s going to be use to ENCRYPT your database, so choose a strong one
  3. We then connect to the unencrypted database and run 3 SQL Queries, the ATTACH, the EXPORT which will export the new encrypted database with your data to the path you chose and finally the DETACH to detach the database from the unencrypted file.
  4. And the last command is the one which will set my AppDelegate databasePath property to the new Encrypted Database we created. In my case this is very important because in my classes, I call AppDelegate to get the path to open the database

This part is now complete and you have your encrypted database. If you go to your Application Documents folder you will now see 2 files, gameDefault.sqlite and encrypted.sqlite.
Keep both there, but make sure you are using the encrypted.sqlite one.

Using FMDB to open Encrypted Database

Now comes the easy part. You should already have all your methods to INSERT, UPDATE, CREATE, etc… in your code, so you should need just to add a single line of code after every time you open the database.

[db setKey:@"secretKey"]

.
I’m going to show you 2 examples, one using FMDatabase and another usingFMDatabaseQueue. In the FMDatabase function, you alway call a [db open] method to open your db, and in FMDatabaseQueue you don necessarily open the database directly, you call a queue method that opens the db for you.

If you try to open your encrypted db it’s not going to work because you need your secret key. so add the setKey method after you open your db

    // FMDatabase    FMDatabase *db = [FMDatabase databaseWithPath:[self getDatabasePath]];    [db open];    [db setKey:@"secretKey"];    // FMDatabaseQueue    FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:[self getDatabasePath]];        [queue inDatabase:^(FMDatabase *db) {        [db setKey:@"secretKey"];        ...    }]; 

And you’re done! that should convert your existing DB into an encrypted db.

FYI: I tried opening my encrypted database in several sqlite browser programs and none could open because it was encrypted, but the code is able to insert, update, delete, etc… to your db just fine.
So if you’re still in development and need frequent access to your DB for debugging purposes, leave the encryption part to the end.

I really want to know if you’re able to use this and if you have/had any problems, so please drop a comment below once you try it out!

Thanks

.gm.

Share

Tags: Database, Encryption, FMDB, How To, iOS, Security, SQLCipher, SQLite, Tutorial

0 0
原创粉丝点击