i want to secure my sqlite database store in doc directory. so i used this for encryption.
[fileManager createFileAtPath:databasePath contents:[#"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding] attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey]];
My code is like this
-(void)createDatabase
{
// Setup some globals
NSString *databaseName = #"Sample.sqlite";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success)
return;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
[fileManager createFileAtPath:databasePath contents:[#"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding] attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey]];
}
-(void)opendb
{
sqlite3 * database;
NSString *databasename=#"Sample.sqlite"; // Your database Name.
NSArray * documentpath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSAllDomainsMask, YES);
NSString * DocDir=[documentpath objectAtIndex:0];
NSString * databasepath=[DocDir stringByAppendingPathComponent:databasename];
if(sqlite3_open([databasepath UTF8String], &database) == SQLITE_OK)
{
const char *sqlStatement = "SELECT * FROM Sampletable"; // Your Tablename
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)
{
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
[Rollnumber addObject:[NSNumber numberWithInt:(int)sqlite3_column_text(compiledStatement,0)]];
[Name addObject:[NSString stringWithFormat:#"%s",(char *) sqlite3_column_text(compiledStatement, 1)]];
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
Roll number and name not being access.
but when i removed this line
[fileManager createFileAtPath:databasePath contents:[#"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding] attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey]];
once again properly work but i want to encrypt my database using this code so how may i aceess my sqlite content. means how may i decrypt when i want to use.
please help me to do this.
Thanks in advance.
my db encrypt successfully. but when i want to access the sqlite. not get the data.
so how may i do this thanks in advance.
You should consider using SQLCipher, that should completely do what you want. SQLCipher was already discussed here: Encrypting SQLite database file on iOS
If you want to roll something yourself, you will have to read the full database into memory, use some decryption code of your now and save the encrypted version as you try above. This nevertheless isn't too easy to do if you have to stay within memory limits for example. Also, to access the database you have to encrypt it and probably store temporarily on disk. This makes it accessible again.
Related
Previously I used the SQLite3 library that comes with Xcode, the code to create a database can be found below:
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = dirPaths[0];
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:#"databases/database.sqlite"]];
NSLog(#"DB Path: %#", databasePath);
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO) {
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &myDatabase) == SQLITE_OK) {
char *errMsg;
const char *sql_data = "CREATE TABLE IF NOT EXISTS myTableName.....";
if (sqlite3_exec(myDatabase, sql_data, NULL, NULL, &errMsg) == SQLITE_OK) {
NSLog(#"Database OK");
[self setDatabase];
} else {
NSLog(#"Data Base Fail");
}
sqlite3_close(myDatabase);
} else {
NSLog(#"Database fail");
}
}
Most recently I had the need to encrypt a database file, and performing a brief search on the internet, I found that some sites recommend using SQL Cipher.
I implemented it in my code in the way that is described in this link, and the only thing I had to do was stay with that code (which I stated earlier), and use these two commands after sqlite3_open:
const char* keyTB = [#"MySecretPassword" UTF8String];
sqlite3_key(myDatabase, keyTB, (int)strlen(keyTB));
To check if my database was encrypted I open it with a text editor and I saw this:
øøèDEÆ?>o›$™;⁄iìÚÄ’†í]¥d=ˇÓä\Êź$4áÓÈ?ïÒ[ÅaÚvÁƒ•i%í≈ª¢.£s◊Âc®Øì≈ àÜU—–}Gec‹≥’B∂¡¸¸Æ™√3Ìnú»YÆ"ß
¬?wÚ÷fñoÂ≈ÛͯzÏâ⁄˛Ct°˘ΩfìÙº0ˇfi]
‚ŸSw∂â≤≥‘=�H€BN±HÇûß…∑º.náaߨO¬ˇ¢(B¨‹óµ¬;º‹ÀÒ
Is it really that SQL Cipher encrypted my database (256-bit AES encryption)? or need to do some configuration in my code?
It is recommended that you not directly embed the password to the database, however that is not a strict requirement for SQLCipher to work. Once you have SQLCipher integrated within your application, you do simply just need to key the database with sqlite3_key once you open the connection. To verify the state of an encrypted database, typically one will run hexdump -C on the database file itself, the content should appear indecipherable.
I am updating an entry in a database, the statement works and the code is updated in the database. Once the app has been closed and reopened though the database has not saved. It seems to create a temporary database and then not actually save to the database that the app is reading from.
Here is my code:
-(void)updateDatabase:(int)Level andPlayer:(int)questionID{
DataClass *obj=[DataClass getInstance];
//NSFileManager *filemgr = [NSFileManager defaultManager];
NSString* Database =[NSString stringWithFormat:#"levelProgress.db"];
NSString* databaseP = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:Database];
databasePath = [[NSString alloc]initWithString:databaseP];
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &questionDB) == SQLITE_OK) {
NSString *querySQL = [NSString stringWithFormat:#"UPDATE levelProgress SET completed_questions=completed_questions+1 WHERE level=%d", obj.levelSelected];
const char *query_stmt = [querySQL UTF8String];
sqlite3_prepare_v2(questionDB, query_stmt, -1, &statement, NULL);
if(sqlite3_step(statement)==SQLITE_DONE){
NSLog(#"update worked");
}else{
NSLog(#"did not work");
}
sqlite3_finalize(statement);
sqlite3_close(questionDB);
}
}
Any help with this would be greatly appreciated.
Copy database file databaseP from application bundle into user folder and then update that. You can't update any file in application bundle (they are always read only).
#define kDatabaseName (#"levelProgress.db")
- (void)checkAndCopyDatabaseIfNeeded
{
if (!self.databasePath)
{
// Database should be present in user sandbox at root.
self.databasePath = [NSString pathWithComponents:[NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject], kDatabaseName, nil]];
}
// Check if the file already copied/exists.
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL success = [fileManager fileExistsAtPath:self.databasePath];
if(!success)
{
// Copy the file from app bundle to user sandbox (Files in app bundle can not be edited).
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:kDatabaseName];
#if DEBUG
BOOL isCopied =
#endif
[fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil];
NSAssert(isCopied, #"Problem copying database file to user document folder");
}
}
When the database is in the bundle it is read only. You will need to have your database in your documents directory when writing to it!
Have a look at the accepted answer here: Use and Access Existing SQLite Database on iOS
I am trying to execute a simple hard-coded insert statement for a SqLite database. The code works and I get a success message from my own NSLog, however, no records are added to the database. Can anyone help? THx! Viv
-(void)addFavorites{
const char *sqlInsert = "insert into rivers (stat_ID, stat_Name, state) values ('03186500','WILLIAMS RIVER','WA')";
sqlite3_stmt *statement;
sqlite3_prepare_v2(_database, sqlInsert, -1, &statement, NULL);
if(sqlite3_step(statement) == SQLITE_DONE){
NSLog(#"RECORD ADDED!");
} else {
NSLog(#"RECORD NOT ADDED!");
}
sqlite3_finalize(statement);
}
Do you have code like this in your app delegate to copy the database out of the bundle to your NSDocuments directory? Be sure to copy the database to there, then point to there when you're running your sqlite3_open, not to the bundle. The NSDocument directory will be saved when the device is synced to iTunes or iCloud, so it's the place you want your database to be for maintaining data.
NSString *databaseName = #"MyDatabase.sqlite";
NSArray *systemPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = [systemPaths objectAtIndex:0];
NSString *databaseFullPath = [libraryDirectory stringByAppendingFormat:#"%#%#",#"/",databaseName];
//copy the database to the file system if it hasn't been done yet.
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL exists = [fileManager fileExistsAtPath:databaseFullPath];
if(exists == NO)
{
NSString *dbPathInBundle = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
[fileManager copyItemAtPath:dbPathInBundle toPath:databaseFullPath error:nil];
}
I want open a sqlite db but it is not in my mainBundle but in a server then I don't have this
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"info.sqlite"];
but I have
NSString *filePath = #"http://serverAddress/info.sqlite";
NSURL *url = [NSURL URLWithString:filePath];
NSData *myData = [NSData dataWithContentsOfURL:url];
and when I read this db what I can do?
If my db is inside mainBundle I do this:
if (sqlite3_open([filePath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select id, one, two from TABLE";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
idNum = [NSNumber numberWithInt:(int)sqlite3_column_int(selectstmt, 0)];
...
...
but if I have a NSData? Can you help me?
SQLite is "server-less", you cannot access from your device to a SQLite file hosted in a server. If it's read-only database, you can download it first or you can use a different database server (and client)
To save the file to documents directory and later read it, add the following code:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString* filePath = [documentsDirectory stringByAppendingPathComponent:#"database.sqlite"];
[data writeToFile:filePath atomically:NO];
Probably your best bet is to publish a thin web-service wrapper around the database. There are more pros that cons using a web-service instead of direct remote database access, take a look to this blog post for further explanations.
I guess you want to download the latest database bundle instead of bundling it when you releasing the application, so now you have downloaded the database file and it is available in NSData format.
If so the slution is simple, you can simply save the NSData to file in to your document directory (Refer this how to save NSData to file), and contnue loading your bundle as normal way. like this,
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"info.sqlite"];
remember before you load file "info.sqlite", you need to save to the file from NSData. I haven't try this, but it should work :)
Happy coding!!!
I am trying to to make a connection to a database and I'm finding that it is successful when I make the path go to NSBundle, but not when I try make the path be in my app's documents directory. Here is my code:
-(IBAction)setInput:(id)sender
{
NSString *strStoreNumber;
NSString *strRegNumber;
strStoreNumber = StoreNumber.text;
strRegNumber = RegNumber.text;
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths lastObject];
NSString* databasePath = [documentsDirectory stringByAppendingPathComponent:#"tblStore.sqlite"];
// NSString* databasePath = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"Opened sqlite database at %#", databasePath);
sqlite3_exec(database, "CREATE TABLE IF NOT EXISTS tblStore (ID INTEGER PRIMARY KEY AUTOINCREMENT, Message TEXT)", NULL, NULL, NULL);
//...stuff
}
else
{
NSLog(#"Failed to open database at %# with error %s", databasePath, sqlite3_errmsg(database));
sqlite3_close (database);
}
//
NSString *querystring;
// create your statement
querystring = [NSString stringWithFormat:#"SELECT strStore, strReg FROM tblStore WHERE strStore = %# AND strReg = %#;", strStoreNumber, strRegNumber];
const char *sql = [querystring UTF8String];
NSString *szStore = nil;
NSString *szReg = nil;
sqlite3_stmt *statement = nil;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL)!=SQLITE_OK) //queryString = Statement
{
NSLog(#"sql problem occured with: %s", sql);
NSLog(#"%s", sqlite3_errmsg(database));
}
else
{
// you could handle multiple rows here
while (sqlite3_step(statement) == SQLITE_ROW)
{
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)];
}
}
sqlite3_finalize(statement);
lblStoreNumber.text = szStore;
lblRegNumber.text = szReg;
//
}
I commented out the line:
NSString* databasePath = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
When this line is NOT commented out, and the lines above it ARE commented out:
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths lastObject];
NSString* databasePath = [documentsDirectory stringByAppendingPathComponent:#"tblStore.sqlite"];
Then it works fine. However, if those three lines are not commented out (as shown in the setInput method, then I get the following errors:
2012-05-07 13:44:29.511 CCoDBTry[1981:f803] Opened sqlite database at /Users/Matt****/Library/Application Support/iPhone Simulator/5.1/Applications/5DB7A218-A0F6- 485F-B366-91FD2F9BC062/Documents/tblStore.sqlite
2012-05-07 13:44:29.545 CCoDBTry[1981:f803] sql problem occured with: SELECT strStore, strReg FROM tblStore WHERE strStore = 8053 AND strReg = 4;
2012-05-07 13:44:29.546 CCoDBTry[1981:f803] no such column: strStore
Keep in mind, this same database table is accessed and works just fine when I use the NSBundle logic. I admit I don't fully understand the difference between NSBundle and documentsDirectory, but I think I would want my table to exist in my app's documents. I would greatly appreciate any help on this.
Thanks!
NSBundle is used to access resources within your application itself: that is, everything inside YourApp.app. The documentsDirectory is a location outside of your app -- it's in the "home directory" which is part of your app sandbox, and which is analogous to your user home directory on the Mac.
These are different locations, so using one to find a file at the same subpath of another won't work.
What #rickster is saying is this:
If you add the sqlite database to your project in Xcode, the database's file gets added to your app's bundle.
If you create the database in code, the file will (most likely) get created in your documents directory (but surely not in your bundle).
The two locations are completely separate. Your bundle is created when your app is compiled and cannot be changed later. Your documents directory is "sandboxed", which allows you access to it; you can read/write/delete files here.
Copy from NSBUndle TO NSDocumentDirectory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"culinary-Info.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"culinary-Info" ofType:#"sqlite"];
[fileManager copyItemAtPath:bundle toPath:path error:nil];
}