iossqliteencryptionfmdbsqlcipher

iOS sqlcipher fmdb “File is encrypted or is not a database”


This tutorial works just fine with the following pieces of code.

pod 'FMDB/SQLCipher'

...

- (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];

...
}

...

-(void) createAndCheckDatabase
{
    BOOL success;

    NSFileManager *fileManager = [NSFileManager defaultManager];
    success = [fileManager fileExistsAtPath:self.databasePath];

    if(success) return; // If file exists, dont 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

    // 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"];
}

...

[db setKey:@"secretKey"]

...

   // 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"];
        ...
    }];

...

When I use the same code in my other project it works fine for encrypting the existing sqlite database. But when I try to access table via sql select query I get the error, "File is encrypted or is not a database". Though, no issue with the tutorial app and I can update/insert/delete/select records there. Any clue?


Solution

  • I've got it fixed. As mentioned the same code is acting differently so there might be something wrong with the settings. In my app, I added the SQLCipher and FMDB as follows:

    pod 'SQLCipher', '~> 3.1'
    pod 'FMDB', '~> 2.5'
    

    while in that tutorial they are being added as

    pod 'FMDB/SQLCipher'
    

    Though, this pod command download the same versions of SQLCipher and FMDB as per the terminal output shown here

    Updating local specs repositories
    Analyzing dependencies
    Downloading dependencies
    Installing FMDB (2.5)
    Installing SQLCipher (3.1.0)
    Generating Pods project
    Integrating client project
    

    So, I changed the pod command like that of the tutorial and that error is fixed. Though, I don't know how these commands differ.