Skip to content

Commit cd6fed5

Browse files
Add support for keying database from byte array.
1 parent bf45bb3 commit cd6fed5

File tree

2 files changed

+100
-34
lines changed

2 files changed

+100
-34
lines changed

android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabase.java

Lines changed: 88 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,7 +1182,7 @@ public static SQLiteDatabase openDatabase(String path, String password, CursorFa
11821182
return openDatabase(path, password == null ? null : password.toCharArray(), factory, flags, hook, errorHandler);
11831183
}
11841184

1185-
/**
1185+
/**
11861186
* Open the database according to the flags {@link #OPEN_READWRITE}
11871187
* {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}
11881188
* with optional hook to run on pre/post key events.
@@ -1206,6 +1206,34 @@ public static SQLiteDatabase openDatabase(String path, String password, CursorFa
12061206
*/
12071207
public static SQLiteDatabase openDatabase(String path, char[] password, CursorFactory factory, int flags,
12081208
SQLiteDatabaseHook hook, DatabaseErrorHandler errorHandler) {
1209+
byte[] keyMaterial = getBytes(password);
1210+
return openDatabase(path, keyMaterial, factory, flags, hook, errorHandler);
1211+
}
1212+
1213+
/**
1214+
* Open the database according to the flags {@link #OPEN_READWRITE}
1215+
* {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}
1216+
* with optional hook to run on pre/post key events.
1217+
*
1218+
* <p>Sets the locale of the database to the the system's current locale.
1219+
* Call {@link #setLocale} if you would like something else.</p>
1220+
*
1221+
* @param path to database file to open and/or create
1222+
* @param password to use to open and/or create database file (byte array)
1223+
* @param factory an optional factory class that is called to instantiate a
1224+
* cursor when query is called, or null for default
1225+
* @param flags to control database access mode and other options
1226+
* @param hook to run on pre/post key events (may be null)
1227+
* @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite reports database
1228+
* corruption (or null for default).
1229+
*
1230+
* @return the newly opened database
1231+
*
1232+
* @throws SQLiteException if the database cannot be opened
1233+
* @throws IllegalArgumentException if the database path is null
1234+
*/
1235+
public static SQLiteDatabase openDatabase(String path, byte[] password, CursorFactory factory, int flags,
1236+
SQLiteDatabaseHook hook, DatabaseErrorHandler errorHandler) {
12091237
SQLiteDatabase sqliteDatabase = null;
12101238
DatabaseErrorHandler myErrorHandler = (errorHandler != null) ? errorHandler : new DefaultDatabaseErrorHandler();
12111239

@@ -1255,16 +1283,16 @@ public static SQLiteDatabase openOrCreateDatabase(File file, String password, Cu
12551283
/**
12561284
* Equivalent to openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook).
12571285
*/
1258-
public static SQLiteDatabase openOrCreateDatabase(String path, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook) {
1259-
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook);
1286+
public static SQLiteDatabase openOrCreateDatabase(File file, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook,
1287+
DatabaseErrorHandler errorHandler) {
1288+
return openOrCreateDatabase(file == null ? null : file.getPath(), password, factory, databaseHook, errorHandler);
12601289
}
12611290

12621291
/**
12631292
* Equivalent to openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook).
12641293
*/
1265-
public static SQLiteDatabase openOrCreateDatabase(File file, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook,
1266-
DatabaseErrorHandler errorHandler) {
1267-
return openOrCreateDatabase(file == null ? null : file.getPath(), password, factory, databaseHook, errorHandler);
1294+
public static SQLiteDatabase openOrCreateDatabase(String path, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook) {
1295+
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook);
12681296
}
12691297

12701298
public static SQLiteDatabase openOrCreateDatabase(String path, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook,
@@ -1281,6 +1309,15 @@ public static SQLiteDatabase openOrCreateDatabase(String path, char[] password,
12811309
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook, errorHandler);
12821310
}
12831311

1312+
public static SQLiteDatabase openOrCreateDatabase(String path, byte[] password, CursorFactory factory, SQLiteDatabaseHook databaseHook) {
1313+
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook, null);
1314+
}
1315+
1316+
public static SQLiteDatabase openOrCreateDatabase(String path, byte[] password, CursorFactory factory, SQLiteDatabaseHook databaseHook,
1317+
DatabaseErrorHandler errorHandler) {
1318+
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook, errorHandler);
1319+
}
1320+
12841321
/**
12851322
* Equivalent to openDatabase(file.getPath(), password, factory, CREATE_IF_NECESSARY).
12861323
*/
@@ -1302,6 +1339,13 @@ public static SQLiteDatabase openOrCreateDatabase(String path, char[] password,
13021339
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, null);
13031340
}
13041341

1342+
/**
1343+
* Equivalent to openDatabase(path, password, factory, CREATE_IF_NECESSARY).
1344+
*/
1345+
public static SQLiteDatabase openOrCreateDatabase(String path, byte[] password, CursorFactory factory) {
1346+
return openDatabase(path, password, factory, CREATE_IF_NECESSARY, null, null);
1347+
}
1348+
13051349
/**
13061350
* Create a memory backed SQLite database. Its contents will be destroyed
13071351
* when the database is closed.
@@ -2489,6 +2533,11 @@ public SQLiteDatabase(String path, char[] password, CursorFactory factory, int f
24892533
this.openDatabaseInternal(password, databaseHook);
24902534
}
24912535

2536+
public SQLiteDatabase(String path, byte[] password, CursorFactory factory, int flags, SQLiteDatabaseHook databaseHook) {
2537+
this(path, factory, flags, null);
2538+
this.openDatabaseInternal(password, databaseHook);
2539+
}
2540+
24922541
/**
24932542
* Private constructor (without database password) which DOES NOT attempt to open the database.
24942543
*
@@ -2517,37 +2566,44 @@ private SQLiteDatabase(String path, CursorFactory factory, int flags, DatabaseEr
25172566
}
25182567

25192568
private void openDatabaseInternal(final char[] password, SQLiteDatabaseHook hook) {
2520-
boolean shouldCloseConnection = true;
25212569
final byte[] keyMaterial = getBytes(password);
2570+
openDatabaseInternal(keyMaterial, hook);
2571+
}
2572+
2573+
private void openDatabaseInternal(final byte[] password, SQLiteDatabaseHook hook) {
2574+
boolean shouldCloseConnection = true;
25222575
dbopen(mPath, mFlags);
25232576
try {
2524-
25252577
keyDatabase(hook, new Runnable() {
25262578
public void run() {
2527-
if(keyMaterial != null && keyMaterial.length > 0) {
2528-
key(keyMaterial);
2579+
if(password != null && password.length > 0) {
2580+
key(password);
25292581
}
25302582
}
25312583
});
25322584
shouldCloseConnection = false;
25332585

25342586
} catch(RuntimeException ex) {
25352587

2536-
if(containsNull(password)) {
2588+
final char[] keyMaterial = getChars(password);
2589+
if(containsNull(keyMaterial)) {
25372590
keyDatabase(hook, new Runnable() {
25382591
public void run() {
25392592
if(password != null) {
2540-
key_mutf8(password);
2593+
key_mutf8(keyMaterial);
25412594
}
25422595
}
25432596
});
2544-
if(keyMaterial != null && keyMaterial.length > 0) {
2545-
rekey(keyMaterial);
2597+
if(password != null && password.length > 0) {
2598+
rekey(password);
25462599
}
25472600
shouldCloseConnection = false;
25482601
} else {
25492602
throw ex;
25502603
}
2604+
if(keyMaterial != null && keyMaterial.length > 0) {
2605+
Arrays.fill(keyMaterial, (char)0);
2606+
}
25512607

25522608
} finally {
25532609
if(shouldCloseConnection) {
@@ -2556,9 +2612,6 @@ public void run() {
25562612
mTimeClosed = getTime();
25572613
}
25582614
}
2559-
if(keyMaterial != null && keyMaterial.length > 0) {
2560-
Arrays.fill(keyMaterial, (byte) 0);
2561-
}
25622615
}
25632616

25642617
}
@@ -2838,6 +2891,24 @@ public synchronized void setMaxSqlCacheSize(int cacheSize) {
28382891
mMaxSqlCacheSize = cacheSize;
28392892
}
28402893

2894+
public static byte[] getBytes(char[] data) {
2895+
if(data == null || data.length == 0) return null;
2896+
CharBuffer charBuffer = CharBuffer.wrap(data);
2897+
ByteBuffer byteBuffer = Charset.forName(KEY_ENCODING).encode(charBuffer);
2898+
byte[] result = new byte[byteBuffer.limit()];
2899+
byteBuffer.get(result);
2900+
return result;
2901+
}
2902+
2903+
public static char[] getChars(byte[] data){
2904+
if(data == null || data.length == 0) return null;
2905+
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
2906+
CharBuffer charBuffer = Charset.forName(KEY_ENCODING).decode(byteBuffer);
2907+
char[] result = new char[charBuffer.limit()];
2908+
charBuffer.get(result);
2909+
return result;
2910+
}
2911+
28412912
private void beginTransactionWithListenerInternal(SQLiteTransactionListener transactionListener,
28422913
SQLiteDatabaseTransactionType transactionType) {
28432914
lockForced();
@@ -2993,15 +3064,6 @@ private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbO
29933064
return attachedDbs;
29943065
}
29953066

2996-
private byte[] getBytes(char[] data) {
2997-
if(data == null || data.length == 0) return null;
2998-
CharBuffer charBuffer = CharBuffer.wrap(data);
2999-
ByteBuffer byteBuffer = Charset.forName(KEY_ENCODING).encode(charBuffer);
3000-
byte[] result = new byte[byteBuffer.limit()];
3001-
byteBuffer.get(result);
3002-
return result;
3003-
}
3004-
30053067
private Pair<Boolean, String> getResultFromPragma(String command) {
30063068
Cursor cursor = rawQuery(command, new Object[]{});
30073069
if(cursor == null) return new Pair(false, "");
@@ -3084,10 +3146,6 @@ private Pair<Boolean, String> getResultFromPragma(String command) {
30843146

30853147
private native int native_status(int operation, boolean reset);
30863148

3087-
private native void native_key(char[] key) throws SQLException;
3088-
3089-
private native void native_rekey(String key) throws SQLException;
3090-
30913149
private native void key(byte[] key) throws SQLException;
30923150
private native void key_mutf8(char[] key) throws SQLException;
30933151
private native void rekey(byte[] key) throws SQLException;

android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteOpenHelper.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,12 @@ public SQLiteOpenHelper(Context context, String name, CursorFactory factory,
130130
public synchronized SQLiteDatabase getWritableDatabase(String password) {
131131
return getWritableDatabase(password == null ? null : password.toCharArray());
132132
}
133-
133+
134134
public synchronized SQLiteDatabase getWritableDatabase(char[] password) {
135+
return getWritableDatabase(password == null ? null : SQLiteDatabase.getBytes(password));
136+
}
137+
138+
public synchronized SQLiteDatabase getWritableDatabase(byte[] password) {
135139
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
136140
return mDatabase; // The database is already open for business
137141
}
@@ -152,7 +156,7 @@ public synchronized SQLiteDatabase getWritableDatabase(char[] password) {
152156
try {
153157
mIsInitializing = true;
154158
if (mName == null) {
155-
db = SQLiteDatabase.create(null, password);
159+
db = SQLiteDatabase.create(null, "");
156160
} else {
157161
String path = mContext.getDatabasePath(mName).getPath();
158162
File dbPathFile = new File (path);
@@ -219,8 +223,12 @@ public synchronized SQLiteDatabase getWritableDatabase(char[] password) {
219223
public synchronized SQLiteDatabase getReadableDatabase(String password) {
220224
return getReadableDatabase(password == null ? null : password.toCharArray());
221225
}
222-
226+
223227
public synchronized SQLiteDatabase getReadableDatabase(char[] password) {
228+
return getReadableDatabase(password == null ? null : SQLiteDatabase.getBytes(password));
229+
}
230+
231+
public synchronized SQLiteDatabase getReadableDatabase(byte[] password) {
224232
if (mDatabase != null && mDatabase.isOpen()) {
225233
return mDatabase; // The database is already open for business
226234
}
@@ -252,7 +260,7 @@ public synchronized SQLiteDatabase getReadableDatabase(char[] password) {
252260
mIsInitializing = true;
253261
db.close();
254262
}
255-
db = SQLiteDatabase.openDatabase(path, password, mFactory, SQLiteDatabase.OPEN_READONLY);
263+
db = SQLiteDatabase.openDatabase(path, password, mFactory, SQLiteDatabase.OPEN_READONLY, mHook, mErrorHandler);
256264
if (db.getVersion() != mNewVersion) {
257265
throw new SQLiteException("Can't upgrade read-only database from version " +
258266
db.getVersion() + " to " + mNewVersion + ": " + path);

0 commit comments

Comments
 (0)