Sqlite Encryption for Android

sqlite encryption for android

Try the SQLCipher port to Android instead of the regular SQLCipher.

encrypt sqlite database Android:

Step #0: Add the code to your UI to prompt the user to enter a passphrase.

Step #1: Download the SQLCipher for Android ZIP file.

Step #2: UnZIP the ZIP file and navigate to the directory that has an assets/ and a libs/ folder.

Step #3: Copy the contents of the assets/ directory into your project's assets/ directory.

Step #4: Copy the contents of the libs/ directory into your project's libs/ directory. Gradle/Android Studio users will also need to add a line to the top-level dependencies closure loading up the contents of libs/, if you do not have one already.

Step #5: Replace all relevant android.database.* and android.database.sqlite.* imports with their SQLCipher for Android equivalents. If you are using an IDE that can help you resolve missing imports (e.g., Ctrl-Shift-O in Eclipse), the easiest thing to do is to get rid of all existing android.database.* and android.database.sqlite.* imports and let the IDE help resolve them. Choose the net.sqlcipher imports when given the choice.

Step #6: You will now have compiler errors on a few methods where you open a database (e.g., getReadableDatabase() on SQLiteOpenHelper), where you now need to pass in the passphrase you collected from the user in Step #0.

This will work for new apps starting up with new databases. There is additional work involved to upgrade an existing app with existing users, if you want to allow those users to switch to an encrypted database.

Encrypt data in SQLite

I use the following for encrypting/decrypting at a column level, i.e. it is only applied to the sensitive data.

class EncryptDecrypt {
private Cipher cipher;
private static SecretKeySpec secretKeySpec;
private static IvParameterSpec ivParameterSpec;
private boolean do_encrypt = true;

/**
* Construct EncryptDecrypt instance that checks the current logged-in
* user status; basically if the user is the special NOUSER, where
* the user does not use a password, then encryption decryption is
* skipped, the alternative constructor does not undergo this check and
* thus will always encrypt (see alternative)
* @param context The context, required for database usage (user)
* @param skey The secret key to be used to encrypt/decrypt
* @param userid The userid (so that the salt can be obtained)
*/
EncryptDecrypt(Context context, String skey, long userid) {
DBUsersMethods users = new DBUsersMethods(context);
if (MainActivity.mLoginMode == LoginActivity.LOGINMODE_NONE) {
do_encrypt = false;
return;
}
String saltasString = users.getUserSalt(userid);
String paddedskey = (skey + saltasString).substring(0,16);

secretKeySpec = new SecretKeySpec(paddedskey.getBytes(),"AES/CBC/PKCS5Padding");
ivParameterSpec = new IvParameterSpec((saltasString.substring(0,16)).getBytes());
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (Exception e){
}
}

/**
* Construct EncryptDecrypt instance that does not check user login-in
* mode, thus the assumption is that this user is NOT the special user
* NOUSER that doesn't require a password to login; this constructor
* is designed to ONLY be used when a user has been added by NOUSER,
* and to then encrypt the data using the enccryptForced method solely
* to encrypt any existing card data for the new user that has a password.
*
* @param context The context, required for database usage (user)
* @param skey The secret key to be used to encrypt/decrypt
* @param userid The userid (so that the salt can be obtained)
* @Param mode Not used other than to distinguish constructor
*/
EncryptDecrypt(Context context, String skey, long userid, boolean mode) {
DBUsersMethods users = new DBUsersMethods(context);
String saltasString = users.getUserSalt(userid);
String paddedskey = (skey + saltasString).substring(0,16);
secretKeySpec = new SecretKeySpec(paddedskey.getBytes(),"AES/CBC/PKCS5Padding");
ivParameterSpec = new IvParameterSpec((saltasString.substring(0,16)).getBytes());
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (Exception e){
//e.printStackTrace();
}
}

/**
* Normal encryption routine that will not encrypt data if the user is
* the special case NOUSER (i.e LOGIN mode is NOUSER), otherwise data
* is encrypted.
*
* @Param toEncrypt The string to be encrypted
* @return The encryted (or not if NOUSER) data as a string
*/
String encrypt(String toEncrypt) {
if (!do_encrypt) {
return toEncrypt;
}
byte[] encrypted;
try {
cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec,ivParameterSpec);
encrypted = cipher.doFinal(toEncrypt.getBytes());
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return Base64.encodeToString(encrypted,Base64.DEFAULT);
}

/**
* Encryption, irrespective of the USER type, noting that this should
* only be used in conjunction with an EncryptDecrypt instance created
* using the 2nd/extended constructor
*
* @param toEncrypt The string to be encrypted
* @return The encrypted data as a string
*/
String encryptForced(String toEncrypt) {
byte[] encrypted;
try {
cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec,ivParameterSpec);
encrypted = cipher.doFinal(toEncrypt.getBytes());
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return Base64.encodeToString(encrypted,Base64.DEFAULT);
}

/**
* Decrypt an encrypted string
* @param toDecrypt The encrypted string to be decrypted
* @return The decrypted string
*/
String decrypt(String toDecrypt) {
if (!do_encrypt) {
return toDecrypt;
}
byte[] decrypted;
try {
cipher.init(Cipher.DECRYPT_MODE,secretKeySpec,ivParameterSpec);
decrypted = cipher.doFinal(Base64.decode(toDecrypt,Base64.DEFAULT));
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return new String(decrypted);
}
}

Example usage (encryption) :-

    ........
db.beginTransaction();
........
newed = new EncryptDecrypt(mContext,newPassword,newUserId,true);

// Process the MatrixCursor created above
while (mxc.moveToNext()) {
// Generate the whereclause to determine which row is to be updated
whereclause = DBCardsTableConstants.CARDID.getDBColumnName() + "=?";
// Set the value to replace the ? plcaeholder with the current CARDID
whereargs = new String[]{
Long.toString(
mxc.getLong(
mxc.getColumnIndex(
DBCardsTableConstants.CARDID.getDBColumnName()
)
)
)
};

String mxc_nameoncard = mxc.getString(mxc.getColumnIndex(DBCardsTableConstants.CARDNAMEONCARD.getDBColumnName()));
String mxc_number = mxc.getString(mxc.getColumnIndex(DBCardsTableConstants.CARDNUMBER.getDBColumnName()));
String mxc_cvv = mxc.getString(mxc.getColumnIndex(DBCardsTableConstants.CARDCVVCODE.getDBColumnName()));
String mxc_pin = mxc.getString(mxc.getColumnIndex(DBCardsTableConstants.CARDPIN.getDBColumnName()));
String mxc_exp = mxc.getString(mxc.getColumnIndex(DBCardsTableConstants.CARDEXPIRYDATE.getDBColumnName()));
String enc_nameoncard = newed.encryptForced(mxc_nameoncard);
String enc_number = newed.encryptForced(mxc_number);
String enc_cvv = newed.encryptForced(mxc_cvv);
String enc_pin = newed.encryptForced(mxc_pin);
String enc_exp = newed.encryptForced(mxc_exp);

// Prepare the data to be updated.
ContentValues cv = new ContentValues();
cv.put(DBCardsTableConstants.CARDOWNER.getDBColumnName(),
newUserId
);
cv.put(DBCardsTableConstants.CARDNAMEONCARD.getDBColumnName(),enc_nameoncard);
cv.put(DBCardsTableConstants.CARDNUMBER.getDBColumnName(),enc_number);
cv.put(DBCardsTableConstants.CARDCVVCODE.getDBColumnName(),enc_cvv);
cv.put(DBCardsTableConstants.CARDPIN.getDBColumnName(),enc_pin);
cv.put(DBCardsTableConstants.CARDEXPIRYDATE.getDBColumnName(),enc_exp);
// Perform the update
db.update(DBCardsTableConstants.CARDS.getDBTableName(),
cv,
whereclause,
whereargs
);
}
// Done with the MatrixCursor so close it
mxc.close();
// Done updating so apply all the changes
db.setTransactionSuccessful();
// Done with the transaction
db.endTransaction();

Example Usage (decryption)

Cursor getDecyrptedCard(long cardid) {
EncryptDecrypt ed = new EncryptDecrypt(mContext,
LoginActivity.getCurrentUserPassWord(),
MainActivity.mCurrentUserid);
MatrixCursor cnvcsr = new MatrixCursor(mCardsTableColumns,10);
String whereclause = DBCardsTableConstants.CARDID.getDBColumnName() +
"=? AND " +
DBCardsTableConstants.CARDOWNER.getDBColumnName() +
"=?";
String[] whereargs = {Long.toString(cardid), Long.toString(MainActivity.mCurrentUserid)};
Cursor basecsr = db.query(DBCardsTableConstants.CARDS.getDBTableName(),
null,
whereclause,
whereargs,
null,null,null,null);
// Check to see of card exists (always should)
if (!basecsr.moveToFirst()) {
cnvcsr.addRow(new Object[]{0L,0L,"NOTACARD","NOTACARD","","","","",""});
return cnvcsr;
}
// If Card is for NOUSER then no decryption requires so return
// base cursor after repositioning to before first.
if (MainActivity.getLoginMode() == LoginActivity.LOGINMODE_NONE) {
basecsr.moveToPosition(-1);
return basecsr;
}
// Get data to decrypt
String extracted_cardnameoncard = basecsr.getString(basecsr.getColumnIndex(
DBCardsTableConstants.CARDNAMEONCARD.getDBColumnName()
));
String extracted_cardnumber = basecsr.getString(basecsr.getColumnIndex(
DBCardsTableConstants.CARDNUMBER.getDBColumnName()
));
String extracted_cardcvv = basecsr.getString(basecsr.getColumnIndex(
DBCardsTableConstants.CARDCVVCODE.getDBColumnName()
));
String extracted_cardpin = basecsr.getString(basecsr.getColumnIndex(
DBCardsTableConstants.CARDPIN.getDBColumnName()
));
String extracted_cardexpiry = basecsr.getString(basecsr.getColumnIndex(
DBCardsTableConstants.CARDEXPIRYDATE.getDBColumnName()
));
// Decrypt data
String decrypted_nameoncard = ed.decrypt(extracted_cardnameoncard);
String decrypted_cardnumber = ed.decrypt(extracted_cardnumber);
String deccrypted_cardcvv = ed.decrypt(extracted_cardcvv);
String decrypted_expiry = ed.decrypt(extracted_cardexpiry);
String decrypted_cardpin = ed.decrypt(extracted_cardpin);

// Store decrypted data
cnvcsr.addRow(new Object[]{
basecsr.getLong(
basecsr.getColumnIndex(
DBCardsTableConstants.CARDID.getDBColumnName()
)),
basecsr.getLong(
basecsr.getColumnIndex(
DBCardsTableConstants.CARDTYPEREF.getDBColumnName()
)
),
basecsr.getLong(
basecsr.getColumnIndex(
DBCardsTableConstants.CARDOWNER.getDBColumnName()
)
),
basecsr.getString(
basecsr.getColumnIndex(
DBCardsTableConstants.CARDNAME.getDBColumnName()
)
),
decrypted_nameoncard,
decrypted_cardnumber,
deccrypted_cardcvv,
decrypted_cardpin,
decrypted_expiry,
basecsr.getString(
basecsr.getColumnIndex(
DBCardsTableConstants.CARDNOTES.getDBColumnName()
)
),
basecsr.getInt(
basecsr.getColumnIndex(
DBCardsTableConstants.CARDCOLOUR.getDBColumnName()
)
)
});

basecsr.close();
return cnvcsr;
}

Note! NOUSER is when a single user of the App decides to not use a password/login.

Android and SQLite protection

Well if you want to protect the file must be on "Root" directory of android. For that, you need to download ".DB" file from the server and move it onto "root" directory. However, this needs an internet connection for the first time app running

Another way is to use,

SQliteCrypt - with SQLiteCrypt you can encrypt the SQLite database and User do not need to enter the password you can set the key in java file when extracting the data.that will be more secure, fast and reliable.

Encrypt SQLite database

After some mails, it turns out that SQLCipher has a Community Edition available, that can be used if agreed with the license.

Here is a guide on how to integrate and use it in Android.

Here and here licensing details.

How to encrypt android sqlite database?

Take a look to the SQLCipher project. It is an open source library that provides transparent, secure 256-bit AES encryption of SQLite database files.

The web site offers a tutorial for Android.

How to encrypt SQlite database for android application?

Try

http://sqlcipher.net/sqlcipher-for-android/

But be aware, that you need to use proguard to obfuscate your decryption code and password. Otherwise it would be an easy task to reverse engineer your application and decrypt the database.

The best solution would be if the user needs to enter the password to your database each time so you dont have to store it in your code where a reverse engineer can find it.



Related Topics



Leave a reply



Submit