Cifrado de base de datos de Android


79

Android usa la base de datos SQLite para almacenar datos, necesito cifrar la base de datos SQLite, ¿cómo se puede hacer esto? Entiendo que los datos de la aplicación son privados. Sin embargo, necesito cifrar explícitamente la base de datos SQLite que está usando mi aplicación.

Respuestas:


68

SQLCipher es una extensión SQLite que proporciona cifrado AES transparente de 256 bits de archivos de base de datos.

El sqlcipher anterior, que es el cifrado de base de datos completo de código abierto para SQLite, no estaba disponible para Android. Pero ahora está disponible como versión alfa para la plataforma Android. Los desarrolladores han actualizado la aplicación estándar de Android 'Notepadbot' para usar SQLCipher.

Así que esta es definitivamente la mejor y más simple opción a partir de ahora.


2
SQLCIpher para Android ahora es parte del proyecto oficial SQLCipher: sqlcipher.net/sqlcipher-for-android
Nombre para mostrar

1
La información sobre la licencia está disponible en la página de github github.com/sqlcipher/android-database-sqlcipher/blob/master/…
vaichidrewar

2
@vaichidrewar Encontrará que ese archivo de licencia en particular se aplica solo a la parte de soporte de Android, hay archivos de licencia adicionales para las cosas de SQLCIPHER ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ) así como el Cosas de IBM ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ).
Hamid

1
Para un ejemplo simple en SQLCipher en Android, aquí está el enlace myownandroid.blogspot.in/2013/09/sqlcipher-in-android.html
jrhamza

SQLCipher ralentiza la aplicación alguna solución para eso @vaichidrewar ??
Arsh Kaushal

28

Las bases de datos están encriptadas para prevenir INDIRECT ATTACKS. Este término y las clases: KeyManager.java , Crypto.java se toman del libro de Sheran Gunasekera Android Apps Security . Recomiendo todo este libro a la lectura.

INDIRECT ATTACKSse llaman así, porque el virus no ataca directamente a su aplicación. En cambio, va tras el sistema operativo Android. El objetivo es copiar todas las bases de datos SQLite con la esperanza de que el autor del virus pueda copiar cualquier información confidencial almacenada allí. Sin embargo, si hubiera agregado otra capa de protección, todo lo que el autor del virus vería son datos confusos. Construyamos una biblioteca criptográfica que podamos reutilizar en todas nuestras aplicaciones. Comencemos creando un breve conjunto de especificaciones:

  • Utiliza algoritmos simétricos: nuestra biblioteca utilizará un algoritmo simétrico, o cifrado en bloque, para cifrar y descifrar nuestros datos. Nos decidiremos por AES, aunque deberíamos poder modificarlo en una fecha posterior.

  • Utiliza una clave fija: Necesitamos poder incluir una clave que podamos almacenar en el dispositivo que se utilizará para cifrar y descifrar datos.

  • Clave almacenada en el dispositivo: la clave residirá en el dispositivo. Si bien esto es un riesgo para nuestra aplicación desde la perspectiva de los ataques directos, debería ser suficiente para protegernos contra ataques indirectos.

Comencemos con nuestro módulo de administración de claves (ver Listado 1 ). Debido a que planeamos usar una clave fija, no necesitaremos generar una aleatoria como hicimos en los ejemplos anteriores. Por lo tanto, KeyManager realizará las siguientes tareas:

  1. Acepte una clave como parámetro (el setId(byte[] data) método)
  2. Aceptar un vector de inicialización como parámetro (el setIv(byte[] data) método)
  3. Almacene la clave dentro de un archivo en la tienda interna
  4. Recuperar la clave de un archivo en la tienda interna (el getId(byte[] data) método)
  5. Recuperar el IV de un archivo en la tienda interna (el getIv(byte[] data) método)

(Listado 1. El módulo KeyManager KeyManager.java )

    package com.yourapp.android.crypto;

    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import android.content.Context;
    import android.util.Log;

    public class KeyManager {

       private static final String TAG = "KeyManager";
       private static final String file1 = "id_value";
       private static final String file2 = "iv_value";
       private static Context ctx;

       public KeyManager(Context cntx) {
         ctx = cntx;
       }

       public void setId(byte[] data){
         writer(data, file1);
       }

       public void setIv(byte[] data){
         writer(data, file2);
       }

       public byte[] getId(){
         return reader(file1);
       }

       public byte[] getIv(){
         return reader(file2);
       }

       public byte[] reader(String file){
         byte[] data = null;
         try {
           int bytesRead = 0;
           FileInputStream fis = ctx.openFileInput(file);
           ByteArrayOutputStream bos = new ByteArrayOutputStream();
           byte[] b = new byte[1024];
           while ((bytesRead = fis.read(b)) != -1){
             bos.write(b, 0, bytesRead);
           }
           data = bos.toByteArray();
         } catch (FileNotFoundException e) {
           Log.e(TAG, "File not found in getId()");
         } catch (IOException e) {
           Log.e(TAG, "IOException in setId(): " + e.getMessage());
         }
         return data;
       }

       public void writer(byte[] data, String file) {
         try {
           FileOutputStream fos = ctx.openFileOutput(file,
           Context.MODE_PRIVATE);
           fos.write(data);
           fos.flush();
           fos.close();
         } catch (FileNotFoundException e) {
           Log.e(TAG, "File not found in setId()");
         } catch (IOException e) {
           Log.e(TAG, "IOException in setId(): " + e.getMessage());
         }
     }
}

A continuación, hacemos el módulo Crypto (ver Listado 2 ). Este módulo se encarga del cifrado y descifrado. Hemos agregado un método armorEncrypt()y armorDecrypt()al módulo para facilitar la conversión de los datos de la matriz de bytes en datos Base64 imprimibles y viceversa. Usaremos el algoritmo AES con el modo de cifrado Cipher Block Chaining (CBC) y relleno PKCS # 5 .

(Listado 2. El módulo criptográfico Crypto.java )

        package com.yourapp.android.crypto;

        import java.security.InvalidAlgorithmParameterException;
        import java.security.InvalidKeyException;
        import java.security.NoSuchAlgorithmException;
        import javax.crypto.BadPaddingException;
        import javax.crypto.Cipher;
        import javax.crypto.IllegalBlockSizeException;
        import javax.crypto.NoSuchPaddingException;
        import javax.crypto.spec.IvParameterSpec;
        import javax.crypto.spec.SecretKeySpec;
        import android.content.Context;
        import android.util.Base64;

        public class Crypto {

           private static final String engine = "AES";
           private static final String crypto = "AES/CBC/PKCS5Padding";
           private static Context ctx;
           public Crypto(Context cntx) {
             ctx = cntx;
           }

           public byte[] cipher(byte[] data, int mode) throws NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,IllegalBlockSizeException,BadPaddingException,InvalidAlgorithmParameterException {
             KeyManager km = new KeyManager(ctx);
             SecretKeySpec sks = new SecretKeySpec(km.getId(), engine);
             IvParameterSpec iv = new IvParameterSpec(km.getIv());
             Cipher c = Cipher.getInstance(crypto);
             c.init(mode, sks, iv);
             return c.doFinal(data);
           }

           public byte[] encrypt(byte[] data) throws InvalidKeyException,
        NoSuchAlgorithmException, NoSuchPaddingException,
        IllegalBlockSizeException, BadPaddingException,
        InvalidAlgorithmParameterException {
             return cipher(data, Cipher.ENCRYPT_MODE);
           }

           public byte[] decrypt(byte[] data) throws InvalidKeyException,
        NoSuchAlgorithmException, NoSuchPaddingException,
        IllegalBlockSizeException, BadPaddingException,
        InvalidAlgorithmParameterException {
             return cipher(data, Cipher.DECRYPT_MODE);
           }

        public String armorEncrypt(byte[] data) throws InvalidKeyException,NoSuchAlgorithmException,
    NoSuchPaddingException,IllegalBlockSizeException,
    BadPaddingException,InvalidAlgorithmParameterException {
                 return Base64.encodeToString(encrypt(data), Base64.DEFAULT);
               }

         public String armorDecrypt(String data) throws InvalidKeyException,NoSuchAlgorithmException,
    NoSuchPaddingException,IllegalBlockSizeException,
    BadPaddingException,InvalidAlgorithmParameterException {
                 return new String(decrypt(Base64.decode(data, Base64.DEFAULT)));
               }
}

Puede incluir estos dos archivos en cualquiera de sus aplicaciones que requieran que el almacenamiento de datos esté cifrado. Primero, asegúrese de tener un valor para su clave y vector de inicialización, luego llame a cualquiera de los métodos de cifrado o descifrado de sus datos antes de almacenarlos. El Listado 3 y el Listado 4 contienen un simple ejemplo de aplicación de estas clases usando. Creamos una Actividad con 3 Botones Cifrar, Descifrar, Eliminar; 1 EditText para la entrada de datos; 1 TextView para salida de datos.

(Listado 3. Un ejemplo. MainActivity.java )

package com.yourapp.android.crypto;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;


public class MainActivity extends Activity {
    TextView encryptedDataView;
    EditText editInputData;
    private Context cntx;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.cntx = getApplicationContext();
        Button btnEncrypt = (Button) findViewById(R.id.buttonEncrypt);
        Button btnDecrypt = (Button) findViewById(R.id.buttonDecrypt);
        Button btnDelete = (Button) findViewById(R.id.buttonDelete);
        editInputData = (EditText)findViewById(R.id.editInputData) ;
        encryptedDataView = (TextView) findViewById(R.id.encryptView);

        /**********************************************/
            /** INITIALIZE KEY AND INITIALIZATION VECTOR **/
        String key = "12345678909876543212345678909876";
        String iv = "1234567890987654";
        KeyManager km = new KeyManager(getApplicationContext());
        km.setIv(iv.getBytes());
        km.setId(key.getBytes());
        /**********************************************/

        btnEncrypt.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String Data = editInputData.getText().toString();
                String Encrypted_Data = "data";
                try {
                    Crypto crypto = new Crypto(cntx);
                    Encrypted_Data = crypto.armorEncrypt(Data.getBytes());
                }   catch (InvalidKeyException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (NoSuchAlgorithmException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (NoSuchPaddingException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (IllegalBlockSizeException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (BadPaddingException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (InvalidAlgorithmParameterException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    }
                encryptedDataView.setText(Encrypted_Data);
            }
        });

        btnDecrypt.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String Data = encryptedDataView.getText().toString();
                String Decrypted_Data = "data";
                try {
                    Crypto crypto = new Crypto(cntx);
                    Decrypted_Data = crypto.armorDecrypt(Data);
                }   catch (InvalidKeyException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (NoSuchAlgorithmException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (NoSuchPaddingException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (IllegalBlockSizeException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (BadPaddingException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    } catch (InvalidAlgorithmParameterException e) {
                    Log.e("SE3", "Exception in StoreData: " + e.getMessage());
                    }
                encryptedDataView.setText(Decrypted_Data);
            }
        });

        btnDelete.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                encryptedDataView.setText(" Deleted ");
            }
        });

    }

}

(Listado 4. Un ejemplo. Activity_main.xml)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#363636"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/editInputData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:ems="10"
        android:textColor="#FFFFFF" >

        <requestFocus />
    </EditText>

    <TextView
        android:id="@+id/encryptView"
        android:layout_width="fill_parent"
        android:layout_height="100dp"
        android:layout_alignLeft="@+id/editInputData"
        android:layout_alignRight="@+id/editInputData"
        android:layout_below="@+id/buttonEncrypt"
        android:layout_marginTop="26dp"
        android:background="#000008"
        android:text="Encrypted/Decrypted Data View"
        android:textColor="#FFFFFF"
        android:textColorHint="#FFFFFF"
        android:textColorLink="#FFFFFF" />

    <Button
        android:id="@+id/buttonEncrypt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/encryptView"
        android:layout_alignRight="@+id/editInputData"
        android:layout_below="@+id/editInputData"
        android:layout_marginTop="26dp"
        android:text="Encrypt" />

    <Button
        android:id="@+id/buttonDelete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/buttonDecrypt"
        android:layout_alignRight="@+id/buttonDecrypt"
        android:layout_below="@+id/buttonDecrypt"
        android:layout_marginTop="15dp"
        android:text="Delete" />

    <Button
        android:id="@+id/buttonDecrypt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/encryptView"
        android:layout_alignRight="@+id/encryptView"
        android:layout_below="@+id/encryptView"
        android:layout_marginTop="21dp"
        android:text="Decrypt" />

</RelativeLayout>

8
Si la clave está almacenada en el dispositivo, ¿cuáles son los beneficios de cifrar, cifrar datos utilizando esa clave?
minhaz

Cómo configurar y obtener la clave de otro archivo ... ¿puede dar un ejemplo de trabajo? obteniendo NPE al leer (archivo)
Gaju Kollur

13

Si la base de datos será pequeña, entonces puede obtener una pequeña cantidad de seguridad descifrando todo el archivo en una ubicación temporal (no en la tarjeta SD) y luego volviendo a cifrar cuando lo haya cerrado. Problemas: muerte prematura de la aplicación, imagen fantasma en los medios.

Una solución ligeramente mejor para cifrar los campos de datos. Esto causa un problema para las cláusulas WHERE y ORDER BY. Si los campos encriptados deben indexarse ​​para la búsqueda de equivalencia, entonces puede almacenar un hash criptográfico del campo y buscarlo. Pero eso no ayuda con las búsquedas de rango o los pedidos.

Si desea ser más elegante, puede profundizar en el NDK de Android y piratear algunas criptomonedas en el código C para SQLite.

Teniendo en cuenta todos estos problemas y soluciones parciales, ¿está seguro de que realmente necesita una base de datos SQL para la aplicación? Es posible que esté mejor con algo como un archivo que contiene un objeto serializado cifrado.


3

Sin duda, puede tener una base de datos SQLite cifrada en Android. Sin embargo, no puede hacerlo con las clases listas para usar proporcionadas por Google.

Un par de alternativas:

  • Compile su propio SQLite a través del NDK e incluya el códec de encriptación de, por ejemplo, wxSQLite3 (se incluye un códec gratuito en el paquete)
  • SQLCipher ahora incluye soporte para Android

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.