82 votes

Cryptage des bases de données Android

Android utilise une base de données SQLite pour stocker les données, j'ai besoin de crypter la base de données SQLite, comment faire ? Je comprends que les données de l'application sont privées. Cependant, j'ai besoin de crypter explicitement la base de données SQLite que mon application utilise.

72voto

vaichidrewar Points 2154

SQLCipher est une extension SQLite qui fournit un cryptage AES 256 bits transparent des fichiers de base de données.

Auparavant, sqlcipher, qui est une solution Open Source de chiffrement complet des bases de données pour SQLite, n'était pas disponible pour Android. Mais maintenant, il est disponible en version alpha pour la plateforme Android. Les développeurs ont mis à jour l'application Android standard 'Notepadbot' pour utiliser SQLCipher.

Il s'agit donc de l'option la plus simple et la meilleure pour l'instant.

2 votes

SQLCIpher for Android fait désormais partie du projet officiel SQLCipher : sqlcipher.net/sqlcipher-pour-Android

1 votes

Les informations sur la licence sont disponibles sur la page github github.com/sqlcipher/Android-database-sqlcipher/blob/master/

2 votes

@vaichidrewar Vous constaterez que ce fichier de licence particulier ne s'applique qu'à la partie support Android, il existe des fichiers de licence supplémentaires pour le matériel SQLCIPHER ( github.com/sqlcipher/Android-database-sqlcipher/blob/master/ ) ainsi que le matériel IBM ( github.com/sqlcipher/Android-database-sqlcipher/blob/master/ ).

30voto

Plo_Koon Points 614

Les bases de données sont cryptées afin d'empêcher INDIRECT ATTACKS . Ce trimestre et ces cours : KeyManager.java , Crypto.java sont tirés de Sheran Gunasekera livre Sécurité des applications Android . Je recommande tous ce livre à la lecture.

INDIRECT ATTACKS sont ainsi nommés, car le virus ne s'attaque pas directement à votre application. Il s'attaque plutôt au système d'exploitation Android. L'objectif est de copier toutes les bases de données SQLite dans l'espoir que l'auteur du virus puisse copier toute information sensible qui y est stockée. Toutefois, si vous aviez ajouté une autre couche de protection, l'auteur du virus ne verrait que des données déformées. Construisons une bibliothèque cryptographique que nous pourrons réutiliser dans toutes nos applications. Commençons par créer un bref ensemble de spécifications :

  • Utilise des algorithmes symétriques : Notre bibliothèque utilisera un algorithme symétrique, ou chiffrement par blocs, pour crypter et décrypter nos données. Nous opterons pour AES, bien que nous devrions être en mesure de le modifier à une date ultérieure.

  • Utilise une clé fixe : Nous devons être en mesure d'inclure une clé que nous pouvons stocker sur l'appareil et qui sera utilisée pour crypter et décrypter les données.

  • Clé stockée sur l'appareil : La clé sera stockée sur l'appareil. Bien que cela représente un risque pour notre application du point de vue des attaques directes, cela devrait suffire pour nous nous protéger contre les attaques indirectes.

Commençons par notre module de gestion des clés (voir Liste 1 ). Comme nous prévoyons d'utiliser une clé fixe, nous n'aurons pas besoin de générer une clé aléatoire comme nous l'avons fait dans les exemples précédents. Le site KeyManager va donc effectuer les tâches suivantes :

  1. Accepter une clé comme paramètre (le setId(byte[] data) méthode)
  2. Accepter un vecteur d'initialisation en tant que paramètre (la fonction setIv(byte[] data) méthode)
  3. Stocker la clé à l'intérieur d'un fichier dans le magasin interne
  4. Récupérer la clé à partir d'un fichier dans le magasin interne (l'option getId(byte[] data) méthode)
  5. Récupérer l'IV à partir d'un fichier dans le magasin interne (l'option getIv(byte[] data) méthode)

(Liste 1. Le module 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());
         }
     }
}

Ensuite, nous faisons le Crypto (voir Liste 2 ). Ce module se charge du cryptage et du décryptage. Nous avons ajouté un armorEncrypt() et armorDecrypt() au module pour faciliter la conversion des données du tableau d'octets en données imprimables. Base64 et vice versa. Nous utiliserons le AES algorithme avec Mode de chiffrement CBC (Cipher Block Chaining) et Rembourrage PKCS#5 .

(Liste 2. Le module cryptographique 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)));
               }
}

Vous pouvez inclure ces deux fichiers dans toutes vos applications qui nécessitent un stockage de données cryptées. Tout d'abord, assurez-vous que vous avez une valeur pour votre clé et votre vecteur d'initialisation, puis appelez l'une des méthodes de chiffrement ou de déchiffrement sur vos données avant de les stocker. Liste 3 et Liste 4 contenir un exemple d'application simple de ces classes en utilisant. Nous créons une Activité avec 3 Boutons Encrypt, Decrypt, Delete ; 1 EditText pour l'entrée des données ; 1 TextView pour la sortie des données.

(Liste 3. Un exemple. 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 ");
            }
        });

    }

}

(Listing 4. Un exemple. activité_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>

9 votes

Si la clé est stockée dans l'appareil, quel est l'intérêt de crypter, de chiffrer les données en utilisant cette clé ?

0 votes

Comment définir et obtenir la clé d'un autre fichier ? Pouvez-vous donner un exemple de fonctionnement ? obtenant NPE à read(file)

14voto

Mark Borgerding Points 2259

Si la base de données est petite, vous pouvez gagner un peu de sécurité en décryptant le fichier entier dans un emplacement temporaire (pas sur la carte SD), puis en le ré-encryptant lorsque vous l'avez fermé. Problèmes : mort prématurée de l'application, image fantôme sur le support.

Une solution légèrement meilleure pour crypter les champs de données. Cela pose un problème pour les clauses WHERE et ORDER BY. Si les champs cryptés doivent être indexés pour la recherche d'équivalence, vous pouvez stocker un hachage cryptographique du champ et le rechercher. Mais cela n'est d'aucune utilité pour les recherches d'intervalles ou l'ordonnancement.

Si vous voulez être plus fantaisiste, vous pouvez vous plonger dans le NDK d'Android et intégrer de la cryptographie dans le code C de SQLite.

Compte tenu de tous ces problèmes et solutions partielles, êtes-vous sûr d'avoir vraiment besoin d'une base de données SQL pour l'application ? Il serait peut-être préférable d'utiliser quelque chose comme un fichier contenant un objet sérialisé crypté.

4voto

NuSkooler Points 2679

Vous pouvez certainement avoir une base de données SQLite cryptée sur Android. Cependant, vous ne pouvez pas le faire avec les cours fournis par Google.

Quelques alternatives :

  • Compilez votre propre SQLite via le NDK et incluez le codec de cryptage, par exemple, wxSQLite3 (un bon codec gratuit est inclus dans le paquet)
  • SQLCipher inclut désormais la prise en charge d'Android

2voto

Ryan Points 543

Pourquoi ne pas simplement crypter les données qui entrent dans la base de données et ne stocker la clé que pendant un laps de temps assez long avant que l'utilisateur ne la saisisse à nouveau ?

Vérifiez http://www.androidsnippets.org/snippets/39/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X