SQLCipher para Android getReadableDatabase () Sobrehéroe

He modificado mi clase DatabaseHelper para usar la biblioteca SQLCipher.

Para hacer eso, yo:

  • Copiado los activos en mi carpeta de activos y las bibliotecas (armeabi, x86, commons-codec, guava-r09, sqlcipher) en mi carpeta libs.

  • Cambió las importaciones en mi clase DatabaseHelper para que apunten a import net.sqlcipher.database.* lugar.

  • Llame a SQLiteDatabase.loadLibs(getApplicationContext()); Cuando se inicia la aplicación.

  • Modificado las líneas donde llamo getReadableDatabase() y getWriteableDatabase() para que incluyan una frase de contraseña como un parámetro;

Todo parece funcionar bien, ya que los datos se leen / escriben correctamente. Mi problema está relacionado con el rendimiento, ya que mi aplicación puede ejecutar operaciones de base de datos con cierta frecuencia, haciendo que se vuelva lenta (después de migrar a SQLCipher).

Para mis métodos DatabaseHelper, creo que estoy siguiendo el enfoque estándar, por ejemplo:

 /* * Getting all MyObjects */ public List<MyObject> getMyObjects() { List<MyObject> objects = new ArrayList<MyObject>(); String selectQuery = "SELECT * FROM " + TABLE_NAME; Log.v(LOG, selectQuery); // Open SQLiteDatabase db = this.getReadableDatabase("...the password..."); // I know this passphrase can be figured out by decompiling. // Cursor with query Cursor c = db.rawQuery(selectQuery, null); // looping through all rows and adding to list if (c.moveToFirst()) { do { MyObject object = createMyObjectFromCursor(c); // Method that builds MyObject from Cursor data // adding to list objects.add(object); } while (c.moveToNext()); } c.close(); db.close(); return objects; } 

No estoy totalmente familiarizado con la mecánica interna de SQLCipher (por ejemplo, ¿descifra todo el archivo DB cuando llamo a getReadableDatabase() ?) Pero, al depurar, parece que la sobrecarga está en getReadableDatabase(password) y getWritableDatabase(password) , Lo cual tiene sentido si mi suposición anterior es verdadera.

¿Cambiaría esas llamadas a un método DatabaseHelper.open () y DatabaseHelper.close () que serían llamados por las Actividades cada vez que instancien un DatabaseHelper, en lugar de llamarlos a cada método individual, sería una mala práctica? Por favor comparta sus conocimientos sobre cómo abordar este problema.

EDITAR:

He utilizado DDMS para rastrear uno de los métodos y puedo ver que la sobrecarga es de hecho en el SQLiteOpenHelper.getReadableDatabase() (tomar ~ 4 seg. Cada vez). Las consultas parecen trabajar rápido y no creo que tenga que preocuparse por ellos.

Si profundizo las llamadas, siguiendo la que tiene la duración más larga cada vez, termino con:

SQLiteDatabase.OpenOrCreateDatabase -> SqLiteDatabase.openDatabase -> SQLiteDatabase.openDatabase -> SQLiteDatabase.setLocale

Así que el SQLiteDatabase.setLocale(java.util.Locale) parece ser el culpable, ya que está tomando ~ 4 segundos cada vez getReadableDatabase () se llama. He mirado en el origen de SQLiteDatabase y sólo bloquea el DB, llama native_setLocale(locale.toString(), mFlags) (la 4 sec. Overhead se lleva a cabo aquí) y desbloquea el DB.

¿Alguna idea de por qué sucede esto?

El problema de rendimiento que está viendo es más probable debido a la derivación de clave SQLCipher. El funcionamiento de SQLCipher para abrir una base de datos es deliberadamente lento, utilizando PBKDF2 para realizar la derivación clave (es decir, miles de operaciones SHA1) para defenderse de los ataques de fuerza bruta y de diccionario (puede leer más sobre esto en http://sqlcipher.net/design ). Esta actividad se aplazó hasta el primer uso de la base de datos, lo que ocurre en setLocale, por lo que está viendo el problema de rendimiento allí al crear perfiles.

La mejor opción es almacenar en caché la conexión de la base de datos de modo que pueda ser utilizada varias veces sin tener que abrir y llave la base de datos repetidamente. Si esto es posible, abrir la base de datos una vez durante el inicio es el curso de acción preferido. El acceso subsiguiente en el mismo identificador de base de datos no activará la derivación clave, por lo que el rendimiento será mucho más rápido.

Si esto no es posible, la otra opción es desactivar o debilitar la derivación clave. Esto hará que SQLCipher utilice menos rondas de PBKDF2 al derivar la clave. Si bien esto hará que la base de datos abra más rápido, es significativamente más débil desde una perspectiva de seguridad. Por lo tanto, no se recomienda excepto en casos excepcionales. Dicho esto, aquí está la información sobre cómo reducir las iteraciones de KDF:

http://sqlcipher.net/sqlcipher-api/#kdf_iter

¿Desencripta el archivo DB entero cuando llamo a getReadableDatabase ()?

No. Descifra páginas (4KB ??) según sea necesario, sobre la marcha.

Parece que la sobrecarga está en getReadableDatabase (contraseña) y getWritableDatabase (contraseña), lo cual tiene sentido si mi suposición anterior es verdadera

Sólo llame a aquellos una vez para toda la vida de su proceso. Cualquier otra cosa es insegura, ya que requiere que usted mantenga la contraseña alrededor, por encima y más allá de los problemas generales.

Por supuesto, usted parece ser difícil de codificar una contraseña, en cuyo caso todo este cifrado es inútil y una pérdida de tiempo.

Por favor comparta sus conocimientos sobre cómo abordar este problema.

Utilice Traceview para determinar exactamente dónde se gasta su tiempo.

En un punto de referencia que realicé, convirtiendo un benchmark de SQLite en SQLCipher, no pude detectar ninguna sobrecarga de material. E / S de disco inundó la sobrecarga de cifrado, cerca como puedo decir.

En la medida en que un SQLCipher bien escrito para la aplicación de Android aumente la sobrecarga, empeorará las malas operaciones. Por lo tanto, por ejemplo, una consulta que necesita hacer una exploración de tabla ya se apesta; SQLCipher hará que la succión incrementalmente más difícil. La solución es agregar los índices apropiados (o FTS3) según sea necesario para evitar el escaneo de tablas.

  • Ciclo de vida de la base de datos Sqlite? ¿Se elimina cuando se cierra la aplicación?
  • Excepción aleatoria android.database.sqlite.SQLiteException: no se puede abrir el archivo de base de datos
  • Tamaño máximo de la base de datos SQLite en la memoria de una aplicación para Android?
  • Obtener fila aleatoria de SQLite Database Android SDK
  • GreenDAO soporta múltiples relaciones entre tablas
  • SQlite Database: No se puede encontrar el contexto
  • Android / SQLite - Operación de bits en la cláusula WHERE
  • ¿Qué es lo mejor: 1 tabla por registro o 1 tabla con todos los registros vinculados con claves foráneas?
  • Cláusula IN y marcadores de posición
  • Android desventajas de no cerrar el cursor
  • Android sqlite: cómo recuperar datos específicos de la columna en particular?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.