使用SQLCipher加密數據庫


   Xcode中集成了免費的sqlite,但是不提供加密的模塊,突然有一天,蛋疼的客戶要求把數據進行加密,於是乎就尋找使用簡單並且可以把數據遷移過度到加密數據庫的框架。
  SQLCipher是第三方的開源框架,實現對sqlite的加密,官網鏈接: http://sqlcipher.net。下面開始下載並導入框架。(使用命令行下載)
一、使用SQLCipher需要3個文件:sqlcipher,openssl-xcode,openssl-1.0.0e
  下載 openssl-xcode
  
cd ~/Documents/code/SQLCipherApp
git clone https://github.com/sqlcipher/openssl-xcode.git

  下載 sqlcipher

cd ~/Documents/code/SQLCipherApp
git clone https://github.com/sqlcipher/sqlcipher.git

   下載 openssl-1.0.0e

curl -o openssl-1.0.0e.tar.gz http://www.openssl.org/source/openssl-1.0.0e.tar.gz
//解壓
tar xzf openssl-1.0.0e.tar.gz

   把這三個目錄拷貝到工程目錄中

二.配置Xcode

  1、打開Xcode 的設置頁,進入locations ->source trees  ,點擊+號添加項目 ,settingname 和 display name 均設為  “OPENSSL_SRC”  path設置為你工程目錄下openssl-1.0.0e的所在路徑。比如我的路徑是:/Users/henry/Documents/工程公用/SQLCipherApp/openssl-1.0.0e

  

     2、添加項目的引用 ,將文件里的openssl.xcodeproj 和sqlcipher.xcodeproj (分別在openssl-xcode文件和sqlcipher文件下)添加到你的主工程下,建立引用
     3、配置編譯庫,進入項目的工程TARGETS,進入build phases ->target dependencies,添加圖中的兩個項目
  
 
link binary with libraries添加這兩個庫
 
 
 
三、下面舉個使用的例子
首先需要引入頭文件 import<sqlite3.h>
 
//打開數據庫的函數
  +(BOOL) OpenDB {
    NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docPaths = [pathArr objectAtIndex:0];
    NSString *dbPath = [docPaths stringByAppendingFormat:@"/myDB.db"];
    int status = sqlite3_open([dbPath UTF8String], &GB_DB);

    if (status != SQLITE_OK)
    {
        LOG_CINFO(@"打開數據庫出錯!");
        DB_Opened = NO;
        return DB_Opened;
    }
    DB_Opened = YES;
sqlite3_stmt *statement;
#if UseASE
    //驗證sql語句是否成功
    const char *key = [[GlobalData GetInstance].GB_DBKey UTF8String];
    sqlite3_key(GB_DB, key, strlen(key));
#endif
    if(sqlite3_prepare_v2(GB_DB, [sql UTF8String], -1, &statement, nil) != SQLITE_OK)
    {
        LOG_CINFO(@"創建表格失敗!");
        return  NO;
    }

    int success = sqlite3_step(statement);
    sqlite3_finalize(statement);
    //[MyDataBase CloseDB];
    if (success != SQLITE_DONE)
    {
        LOG_CINFO(@"在創建表格的過程中出錯,創建沒有進行完!");
        return NO;
    }
        return YES;
}
//創建需要的數據表
+ (void)CreateNeedTable {
    @autoreleasepool {

        //插入用戶信息表
        LocalDataBase *userTb = [LocalDataBase GetTableWithType:@"user" HasUser:NO];
            //先創建
               [userTb CreateTableWithKeys:[NSArray arrayWithObjects:@"userid", nil] OtherNeeds:[NSArray arrayWithObjects:@"siteid",@"username",@"password",@"mobilePhone",@"name", nil] Data:nil];
}

 

//根據參數創建表,keys是主鍵,needs是那些非主鍵,但是必須需要的,如需要用它來排序,搜索的字段,data為數據項的整個data形式,用data是為了減少字段數
-(BOOL) CreateTableWithKeys:(NSArray *)keys OtherNeeds:(NSArray *)needs Data:(NSString *)data {
    //如果有信息,說明注冊成功了已經
    if(self.myTableInfo)
    {
        return YES;
    }

    //首先確保數據庫是打開的
    if (![LocalDataBase OpenDB])
    {
        return NO;
    }

    //把所有非數據的字段寫到一個數組中

    NSMutableArray *allArr = [NSMutableArray arrayWithCapacity:10];
    if ([keys count] > 0)
    {
        [allArr addObjectsFromArray:keys];
    }

    if ([needs count] > 0)
    {
        [allArr addObjectsFromArray:needs];
    }


    if (self.hasUser)
    {
        [allArr insertObject:@"userName" atIndex:0];
    }

    int keysCount = [allArr count];

    if (self.myTableInfo == nil)
    {
        self.myTableInfo = [NSMutableArray arrayWithCapacity:keysCount+1];
    }
    //插入數據
    for(int i = 0; i < keysCount;++i)
    {
        NSString *key = [allArr objectAtIndex:i];
        [self.myTableInfo addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"text",@"type", key,@"key", nil]];
    }


    //創建sql語句
    NSMutableString *cSql = [NSMutableString stringWithFormat:@"create table if not exists %@ (",self.myTableName];

    //把非數據類型的字段加入sql語句
    for(NSString *key in allArr)
    {
        [cSql appendFormat:@"%@ text,",key];
    }

    //把數據類型的字段加入Sql語句
    if (data != nil)
    {
        [cSql appendFormat:@"%@ blob,",data];
        [self.myTableInfo addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"blob",@"type", data,@"key", nil]];
    }

    //添加主鍵
    [cSql appendString:@"primary key("];

    int keyCount = [keys count];
    if (keyCount > 0)//有多個主鍵的情況
    {
        for(int i = 0; i < keyCount - 1; ++i)
        {
            NSString *key = [keys objectAtIndex:i];
             [cSql appendFormat:@"%@,",key];
        }

        if(self.hasUser)
        {
            [cSql appendString:@"userName,"];
        }
        [cSql appendFormat:@"%@)",[keys objectAtIndex:keyCount - 1]];
    }
    else
    {
//        if (keyCount == 1)//只有一個主鍵的情況
//        {
//            [cSql appendFormat:@"%@)",[keys objectAtIndex:0]];
//        }
//        else
        {
            if(self.hasUser)
            {
                [cSql appendString:@"userName)"];
            }
        }
    }

    [cSql appendString:@")"];

    LOG_CINFO(@"========sql 語句 創建表=========");
    LOG_CINFO(cSql);

    {
        NSMutableDictionary *dic = (NSMutableDictionary *)[GlobalFunc ParseDicFromFile:@"dbInfo.plist"];
        if (dic == nil)
        {
            dic = [NSMutableDictionary dictionaryWithCapacity:1];
        }
        [dic setObject:self.myTableInfo forKey:self.myTableName];
        [GlobalFunc WriteDicToFile:dic FileName:@"dbInfo.plist"];
    }

    return [LocalDataBase CreateTableWithSql:cSql];
}

 

//向表中插入數據
-(BOOL) InsertDataWithDic:(NSDictionary *)dic Replace:(BOOL) replace {
    // 打開數據庫
    if (!DB_Opened)
    {
        if(![LocalDataBase OpenDB])
        {
            LOG_CINFO(@"插入數據失敗,打開數據庫出錯!");
            return NO;
        }
    }

    NSMutableDictionary *tmpDic = [NSMutableDictionary dictionaryWithDictionary:dic];
    if (self.hasUser)
    {
        [tmpDic setObject:[GlobalData GetInstance].GB_UserName forKey:@"userName"];
    }

    NSMutableArray *allKeys = [NSMutableArray arrayWithArray:[tmpDic allKeys]];


    NSMutableString *cSql = nil;
    //生成插入語句
    if (replace)
    {
        cSql = [NSMutableString stringWithFormat:@"insert or REPLACE into %@(",self.myTableName];
    }
    else
    {
        cSql = [NSMutableString stringWithFormat:@"insert into %@(",self.myTableName];
    }

    int keysCount = [allKeys count];

    if (keysCount > 0)
    {
        for(int i = 0; i < keysCount-1;++i)
        {
            [cSql appendFormat:@"%@,",[allKeys objectAtIndex:i]];
        }

        [cSql appendFormat:@"%@)",[allKeys objectAtIndex:keysCount -1]];
    }
    else
    {
        return NO;
    }

    [cSql appendString:@" values("];


    for(int i = 0; i<keysCount -1; ++i)
    {
        [cSql appendString:@"?,"];
    }
    [cSql appendString:@"?)"];

    LOG_CINFO(@"========sql 語句 插入表=========");
    LOG_CINFO(cSql);

    //測試sql 語句是否正確
    sqlite3_stmt *statement;

    const char *insertStatement = [cSql UTF8String];
#if UseASE
    //驗證sql語句是否成功
    const char *key = [[GlobalData GetInstance].GB_DBKey UTF8String];
    sqlite3_key(GB_DB, key, strlen(key));
#endif
    if(sqlite3_prepare_v2(GB_DB, insertStatement, -1, &statement, NULL) != SQLITE_OK)
    {
        LOG_CINFO(@"向表格中插入數據失敗,可能Sql語句不正確!");
        [GlobalFunc ShowNormalAlert:[NSString stringWithFormat:@"向表格中插入數據失敗,可能Sql語句不正確,表名為%@",self.myTableName]];
        return  NO;
    }

    for(int i = 0; i < keysCount;++i)
    {
        NSString *key = [allKeys objectAtIndex:i];

        id value = [tmpDic objectForKey:key];

        //如果是Data類型
        if ([value isKindOfClass:[NSData class]])
        {
            sqlite3_bind_blob(statement,  i+1, [value bytes], [value length], NULL);
        }
        else//是字符串類型
        {
            sqlite3_bind_text(statement, i+1, [value UTF8String], -1, NULL);
        }
    }

    int success = sqlite3_step(statement);
    // 釋放資源
    sqlite3_finalize(statement);

    if (success == SQLITE_ERROR)
    {
        LOG_CINFO(@"向表格中插入數據失敗,未知原因提前結束!");
        [GlobalFunc ShowNormalAlert:[NSString stringWithFormat:@"向表格中插入數據失敗,未知原因提前結束,表名為%@",self.myTableName]];
        return NO;
    }
    LOG_CINFO(@"向表格中插入數據成功!");
    return YES;

}

 

//更新表字段,key是要更新的字段名稱,newValue是更新后要設計的值,where是條件(sql語句中的),condition是滿足更新的條件,use是否使用用戶名為條件
-(BOOL) UpdateRecordWithKey:(NSString *)key Value:(NSString *)newValue Where:(NSString *)where Condition:(NSString *)condition UseUser:(BOOL)use {
    if(![LocalDataBase OpenDB])
    {
        return NO;
    }
    @try
    {
        NSString *tmpUpdateSql = nil;

        if (use && self.hasUser)
        {
            tmpUpdateSql =  [NSString stringWithFormat:@"UPDATE %@ SET %@ = ? where %@ = ? and userName = ?",self.myTableName,key,where];
        }
        else
        {
            tmpUpdateSql = [NSString stringWithFormat:@"UPDATE %@ SET %@ = ? where %@ = ?",self.myTableName,key,where];//@"UPDATE tb_bulletlist SET has_read = ? where bulletin_code = ? and user_name=?";
        }
        sqlite3_stmt *statement;

        LOG_CINFO(@"========sql 語句 更新表=========");
        LOG_CINFO(tmpUpdateSql);
#if UseASE
        //驗證sql語句是否成功
        const char *key = [[GlobalData GetInstance].GB_DBKey UTF8String];
        sqlite3_key(GB_DB, key, strlen(key));
#endif
        if(sqlite3_prepare_v2(GB_DB, [tmpUpdateSql UTF8String], -1, &statement, nil) != SQLITE_OK)
        {
            LOG_CINFO(@"更新數據失敗!");
             [GlobalFunc ShowNormalAlert:[NSString stringWithFormat:@"更新數據失敗,表名為%@",self.myTableName]];
            return  NO;
        }

        sqlite3_bind_text(statement, 1, [newValue UTF8String], -1, NULL);
        sqlite3_bind_text(statement, 2, [condition UTF8String], -1, NULL);

        if (use && self.hasUser)
        {
            sqlite3_bind_text(statement, 3, [[GlobalData GetInstance].GB_UserName UTF8String], -1, NULL);
        }
        int success = sqlite3_step(statement);

        sqlite3_finalize(statement);
        //[MyDataBase CloseDB];
        if (success != SQLITE_DONE)
        {
            LOG_CINFO(@"更新數據失敗,未知原因提前結束!");
             [GlobalFunc ShowNormalAlert:[NSString stringWithFormat:@"更新數據失敗,未知原因提前結束,表名為%@",self.myTableName]];
            return NO;
        }

    }
    @catch (NSException *e)
    {
       // LOG_CERR(e);
    }

    return YES;
}

 

//根據傳入的關鍵字和關鍵字的值,得到一條記錄,如果不存在這條記錄,返回為nil,估也可用來判斷是否存在某條記錄,use是否使用用戶名為條件
-(NSMutableDictionary *) GetOneRecordWithKeys:(NSArray *)keys Values:(NSArray *)values UseUser:(BOOL)use {
    if ([keys count] != [values count])
    {
//        [GlobalFunc ShowNormalAlert:[NSString stringWithFormat:@"GetOneRecordWithKeys 數據查詢參數keys與values個數不一致,表名為%@",self.myTableName]];

        return nil;
    }

    // 打開數據庫
    if (!DB_Opened)
    {
        if(![LocalDataBase OpenDB])
        {
            LOG_CINFO(@"查詢數據失敗,打開數據庫出錯!");
            return nil;
        }
    }

    NSMutableString *cSql = [NSMutableString stringWithFormat:@"select * from %@ where ",self.myTableName];
    if (use && self.hasUser)
    {
        [cSql appendFormat:@"userName = '%@' and ",[GlobalData GetInstance].GB_UserName];
    }

    int keyCount = [keys count];
    for (int i = 0; i < keyCount; ++i)
    {
        if (i == 0)
        {
            [cSql appendFormat:@"%@ = '%@'",[keys objectAtIndex:i],[values objectAtIndex:i]];
        }
        else
        {
            [cSql appendFormat:@" and %@ = '%@'",[keys objectAtIndex:i],[values objectAtIndex:i]];
        }

    }

    NSArray *tmpArr = [self GetDataArrWithSql:cSql];

    if ([tmpArr count] == 0)
    {
        return nil;
    }
    else
    {
        return [tmpArr objectAtIndex:0];
    }
}

 

  如果在編譯時提示:No architectures to compile for (ARCHS=armv6,armv7, VALID_ARCHS=armv7 armv7s則將在Bulid Settings選項下面的Architectures和Valid Architectures里面都改成一樣(例如:都填寫 armv6 armv7),問題解決。 對於警告 :warning: implicit declaration of function 'sqlite3_key' is invalid in C99 只需要將Bulid Settings選項下的C Language Dialect 改為:C89[-std-c89] 就可以,即使用c89標准

或者去掉項目中的arm64

 以上只是貼出代碼的一部分,可能看起來有些吃力,稍后會把一個完整的使用數據庫的類整理出來。

本文參考:http://blog.csdn.net/kuai0705/article/details/8931996

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM