project-imas / encrypted-core-data

v2.0 - iOS Core Data encrypted SQLite store using SQLCipher
Other
785 stars 236 forks source link

Migration Old unencrypted Core database to Encrypted Core Data base(using SQLCipher). #331

Open emorphisgit opened 4 years ago

emorphisgit commented 4 years ago

I have a live iOS application on apple app-store which is using the core data. Now I want to encrypt the core-data using EncryptedCoreData (https://github.com/project-imas/encrypted-core-data 2). I am successfully able to implement the same with my project and it working fine for fresh installed but when I tried to override my existing application which is not using the encrypted coredata with the new application which is using the encrypted coredata at that time I am unable to get my old data back. I don't know what I am doing wrong. Following is my code sample

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (persistentStoreCoordinator_ != nil) {
        return persistentStoreCoordinator_;
    }
    else if (!storeUrl)
        return nil;

    NSError *error = nil;
    persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
    NSDictionary *options = @{          EncryptedStorePassphraseKey : @"123456",
                                        EncryptedStoreFileManagerOption : [EncryptedStoreFileManager defaultManager],
                                        NSMigratePersistentStoresAutomaticallyOption : @YES
                                        };

    NSPersistentStore *store = [persistentStoreCoordinator_
                                addPersistentStoreWithType:EncryptedStoreType
                                configuration:nil
                                URL:storeUrl
                                options:options
                                error:&error];

    if (!store && error)
    {
        [self encryptDB:[storeUrl absoluteString] url:storeUrl];

        NSError *error1 = nil;
        NSPersistentStore *store1 = [persistentStoreCoordinator_
                                    addPersistentStoreWithType:EncryptedStoreType
                                    configuration:nil
                                    URL:storeUrl
                                    options:options
                                    error:&error1];
    }

    return persistentStoreCoordinator_;
}

//encryptDB used for encriptt the existing DB with password
- (void)encryptDB:(NSString*)path_u url:(NSURL*)url
{
    sqlite3 *unencrypted_DB;
    sqlite3 *encrypted_DB;
    NSString *tempFile = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
                        stringByAppendingPathComponent:@"encrypted.sqlite"];

    NSURL *tempUrl = [NSURL fileURLWithPath:tempFile];

    if (sqlite3_open([path_u UTF8String], &unencrypted_DB) == SQLITE_OK) {
        NSLog(@"Database Opened");
        // Attach empty encrypted database to unencrypted database

        NSString *strEncryptedPath = [NSString stringWithFormat:@"ATTACH DATABASE '%@' AS encrypted KEY '123456';",tempFile];

        sqlite3_exec(unencrypted_DB, [strEncryptedPath UTF8String], 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);

        int version = [self queryUserVersion:unencrypted_DB];
        sqlite3_close(unencrypted_DB);
        NSLog (@"End database copying");

        if (sqlite3_open([tempFile UTF8String], &encrypted_DB) == SQLITE_OK) {
                  const char* key = [@"123456" UTF8String];
                  sqlite3_key(encrypted_DB, key, (int)strlen(key));
                 [self setVersion:encrypted_DB version:(int)version];
          }
         sqlite3_close(encrypted_DB);

        NSError *error = nil;
       [[NSFileManager defaultManager] removeItemAtURL:url error:&error];

         BOOL result = [[NSFileManager defaultManager] moveItemAtURL:tempUrl toURL:url error:&error];
        if(!result)
            NSLog(@"Error: %@", error);
    }
    else {
        sqlite3_close(unencrypted_DB);
        //NSAssert1(NO, @"Failed to open database with message '%s'.", sqlite3_errmsg(unencrypted_DB));
    }
}
-(void)setVersion: (sqlite3*) db version:(int)version {

    // get current database version of schema
    static sqlite3_stmt *stmt_version;

    if(sqlite3_prepare_v2(db, "PRAGMA user_version;", -1, &stmt_version, NULL) == SQLITE_OK) {
        while(sqlite3_step(stmt_version) == SQLITE_ROW) {
            db = sqlite3_bind_int( stmt_version, 1, version ); // Bind first parameter.
        }
    } else {
        NSLog(@"%s: ERROR Preparing: , %s", __FUNCTION__, sqlite3_errmsg(db) );
    }
    sqlite3_finalize(stmt_version);
}
-(int)queryUserVersion: (sqlite3*) db {
    // get current database version of schema
    static sqlite3_stmt *stmt_version;
    int databaseVersion;

    if(sqlite3_prepare_v2(db, "PRAGMA user_version;", -1, &stmt_version, NULL) == SQLITE_OK) {
        while(sqlite3_step(stmt_version) == SQLITE_ROW) {
            databaseVersion = sqlite3_column_int(stmt_version, 0);
            NSLog(@"%s: version %d", __FUNCTION__, databaseVersion);
        }
        NSLog(@"%s: the databaseVersion is: %d", __FUNCTION__, databaseVersion);
    } else {
        NSLog(@"%s: ERROR Preparing: , %s", __FUNCTION__, sqlite3_errmsg(db) );
    }
    sqlite3_finalize(stmt_version);

    return databaseVersion;
}

Following code I am using for fetch the data

NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"HistoryEvent" inManagedObjectContext:dbDataFile.managedObjectContext];
    [request setEntity:entity];
NSMutableArray* andPredicateArray = [[NSMutableArray alloc] init];
    if (guid)
    {
        [andPredicateArray addObject:
         [NSPredicate predicateWithFormat:@"guid = %@", guid]];
    }
    NSPredicate* predicate = [NSCompoundPredicate andPredicateWithSubpredicates:andPredicateArray];
    [request setPredicate:predicate];

    [request setFetchLimit:limit];
NSMutableArray* sortDescriptors = [[NSMutableArray alloc] init];
        [sortDescriptors addObject:
         [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:[isAscendingCreationDatesNum boolValue]]];
[request setSortDescriptors:sortDescriptors];
    NSError *error = nil;
    NSMutableArray *mutableFetchResults = [[dbDataFile.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
    if (!mutableFetchResults && errorMessage && error)
    {
        NSLog(@"Error to fetch”); //I am not getting any error here
    }

    return mutableFetchResults; //Count is zero

Can anyone please help me and point out what I am doing wrong. Again If I installed fresh application then the same code is working fine I am only getting empty data when tried to override the existing app i.e installing encrypted coredata app on non-encrypted coredata. I also created an issue at https://discuss.zetetic.net/t/unable-to-fetch-data/4560