[Unix x86][C/CPP]Encrypt and Decrypt a database using sqlCipher API

Team,

Thanks for creating sqlCipher.

I want to encrypt and decrypt a database on unix x86 platform in C/CPP program.

Encryption

sqlite3_open(my_db, &qdb) != SQLITE_OK) 
sqlite3_key(qdb, "ranjan", 6) != SQLITE_OK)
  1. Now to update this db in run time, my C/CPP program will need to decrypt this DB. right?
    • Open the empty DB.
    • Encrypt the DB.
  • Everytime application wants to update this encrypted db;
    • Decrypt the DB,
    • update the table
    • Encrypt the DB again
  1. Which sqlcipher APIs i can use in my C/CPP program to decrypt the DB?
  2. what is license key for opensource community version of sqlcipher?

Thanks in advance for looking into this issue

Hi @ranjankumar23,

Thanks for your interest in SQLCipher! Calling sqlite3_key does not encrypt the database, rather it sets the password material to be used once a read/write operation is required by the database. When a read/write operation is invoked, it will take the password material and determine whether the encryption key needs to be derived, or if a raw key was provided to use that as the encryption key. All subsequent database operations (i.e., INSERT, UPDATE, DELETE, SELECT) will encrypt/decrypt the data on demand for you (presuming you are working on a SQLCipher database.)

You will use the standard SQLite C API [1] for interfacing with the database after it has been keyed. A license code is only required with the Commercial and Enterprise editions of SQLCipher, the Community edition does not require a license code.

As an aside, if you already have a plaintext SQLite database and wish to encrypt it with SQLCipher, you will want to use the sqlcipher_export(...) [2] convenience function.


  1. An Introduction To The SQLite C/C++ Interface ↩︎

  2. SQLCipher API - Zetetic ↩︎

Thank you Nick.

It means if i am creating new encrypted database, then only this function call is required.
sqlite3_key(qdb, “ranjan”, 6)

Everything else will remain same in existing code for all subsequent database operations (i.e., INSERT , UPDATE , DELETE , SELECT ).

Somehow encrypted DB file is getting created, but it is not getting updated. i will debug and come up with more info to seek your help.

Hi @ranjankumar23

You will want to make sure your application is properly linking SQLCipher, instead of SQLite. The recommended approach to doing this is by executing the following SQL statement:

PRAGMA cipher_version;

This will return the SQLCipher library version as a string, however, if you are linking against SQLite, you will not receive a value.

Thanks Nick.
It was issue with incorrect linking of sqlcipher.

Nick,

Now i want to complete this use case.

  1. Create an encrypted db using following APIs.
    sqlite3_open(my_db, &qdb) != SQLITE_OK)
    sqlite3_key(qdb, “ranjan”, 6) != SQLITE_OK)
  2. Do all subsequent database operations (i.e., INSERT , UPDATE , DELETE , SELECT ) using other sqlite3 APIs.
  3. Now change the key for database created in step-1. Is this understanding correct?
    sqlite3_open(my_db, &qdb) != SQLITE_OK)
    sqlite3_key(qdb, “ranjan”, 6) != SQLITE_OK)
    sqlite3_rekey(qdb, “kumar”, 5) != SQLITE_OK)

Hi @ranjankumar23

Yes, in order to perform a rekey operation [1] you must open the database and provide the current key prior to invoking the rekey step.


  1. SQLCipher API - Zetetic ↩︎

Nick, latest sqlcipher 4.5.2 is compatible with openssl 1.0, openssl 1.1 and openssl 3.0 ?

@developernotes Nick, i have last two questions for this thread.

  1. latest sqlcipher 4.5.2 is compatible with openssl 1.0, openssl 1.1 and openssl 3.0 ?
  2. If application create a encrypted db using key ‘KEY1’ and system is rebooted. After reboot application needs to provide the same key ‘KEY1’ before any operation on DB?

Hi @ranjankumar23

SQLCipher can be used with OpenSSL 1.0.*, 1.1.*, however, I cannot speak to the 3.0.* series branch. Anytime a SQLCipher-encrypted database is closed, you will need to provide the same key again prior to any database access on that file.

Thanks Nick. You have been very helpful.

Hi @ranjankumar23

As a follow-up, I have confirmed that OpenSSL 3.0.* series does also work with SQLCipher.

Yes Nick, i am using openssl-3.0 and it has been working without any issue.

Nick @developernotes

I need your help.

I am trying to convert plaintext DB to encrypted DB in C program.

Command line is working fine.

$ ./sqlcipher test-me.db
sqlite> ATTACH DATABASE ‘encrypted.db’ AS encrypted KEY ‘testkey’;
sqlite> SELECT sqlcipher_export(‘encrypted’);
sqlite> DETACH DATABASE encrypted;

But in code it is not working. It is creating encrypted.db with size 0, whereas with command line encrypted.db size is same as test-me.db size.

CODE SNIPPET

if (sqlite3_open("/home/config/test-me.db", &db_persist_1) != SQLITE_OK) {
 
  sqlite3_close(qdb_persist_1);
  qdb_persist_1 = NULL;
  return -1;
}

char *zErrMsg = 0;
const char *sql = " "          \
"CREATE TABLE ABC_KEY("        \
   "ID   KEY  CHAR(128),"  \
   "LEN       UINT(1),"    \
   "TIMESTAMP CHAR(20),"   \
   "a_ID   CHAR(65), UNIQUE(ID));";


/* Execute SQL statement */
if( sqlite3_exec(db_persist_1, (const char*)sql, _callback, 0, &zErrMsg) != SQLITE_OK ){
    sqlite3_free(zErrMsg);
    return QERR_TABLE_CREATE_VOLATILE;
}



const char *sql1 = " "          \
"ATTACH DATABASE 'encrypted.db' AS encrypted KEY 'test123';"  \
   "SELECT sqlcipher_export(encrypted);"  \
   "DETACH DATABASE encrypted;";

/* Execute SQL statement */
if( sqlite3_exec(db_persist_1, (const char*)sql1, _callback, 0, &zErrMsg) != SQLITE_OK ){
    sqlite3_free(zErrMsg);
   return -1;
}

Hi @ranjankumar23

There are a couple of things to check. First, in your C program, change the behavior to execute the following SQL command after you open a database connection:

PRAGMA cipher_version;

Verify that a string value is returned to confirm you are properly linking SQLCipher within the application. Next, execute the individual SQL statements separately and inspect their individual result codes. You might find it easier to create a prepared statement and stepping through it. An introduction to the C/C++ interface can be found here [1].


  1. An Introduction To The SQLite C/C++ Interface ↩︎

@developernotes Nick,

This is the output.
cipher_version = 4.5.2 community
RANJAN-no such column: encrypted

Error is “no such column: encrypted” if i execute this code.

if (sqlite3_open(“/home/config/test-me.db”, &db_persist_1) != SQLITE_OK) {

sqlite3_close(qdb_persist_1);
qdb_persist_1 = NULL;
return -1;
}

const char *sql1 = " "
“ATTACH DATABASE ‘encrypted.db’ AS encrypted KEY ‘qnulabs123’;”
“SELECT sqlcipher_export(encrypted);”
“DETACH DATABASE encrypted;”;

/* Execute SQL statement */
if( sqlite3_exec(qdb_persist_1, (const char*)sql1, _callback, 0, &zErrMsg) != SQLITE_OK ){
   
    printf("RANJAN-%s\n", zErrMsg);
    sqlite3_free(zErrMsg);
  
}

@ranjankumar23 you are missing the Single quote marks around the encrypted parameter to sqlcipher_export().

Thanks Stephen. It is working now.