web-dev-qa-db-fra.com

Android

Android utilise la base de données SQLite pour stocker des données, j'ai besoin de crypter la base de données SQLite, comment procéder? Je comprends que les données d'application sont privées. Cependant, je dois crypter explicitement la base de données SQLite que mon application utilise.

76
user121196

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

La version antérieure de sqlcipher, qui est le cryptage complet de la base de données Open Source pour SQLite, n'était pas disponible pour Android. Mais maintenant, il est disponible en version alpha pour Android. Les développeurs ont mis à jour la norme Android 'Notepadbot' pour utiliser SQLCipher).

C'est donc certainement l'option la meilleure et la plus simple pour l'instant.

66
vaichidrewar

Les bases de données sont cryptées afin d'empêcher INDIRECT ATTACKS. Ce terme et les classes: KeyManager.Java, Crypto.Java sont tirés de Sheran Gunasekera livre Android Sécurité des applications . Je recommande tout ce livre à la lecture.

INDIRECT ATTACKS Sont ainsi nommés, car le virus ne s'en prend pas directement à votre application. Au lieu de cela, il va après le système d'exploitation Android. Le but est de copier toutes les bases de données SQLite dans l'espoir que l'auteur du virus puisse copier toutes les informations sensibles qui y sont stockées. Si vous aviez ajouté une autre couche de protection, cependant, tout ce que l'auteur du virus verrait, ce sont des données brouillées. Construisons une bibliothèque cryptographique que nous pouvons 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 bloc de chiffrement pour crypter et décrypter nos données. Nous nous contenterons d'AES, bien que nous devrions pouvoir le modifier ultérieurement.

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

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

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

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

(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 module Crypto (voir Listing 2 ). Ce module s'occupe du chiffrement et du déchiffrement. Nous avons ajouté une méthode 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 l'algorithme AES avec mode de cryptage Cipher Block Chaining (CBC) et remplissage PKCS # 5 =.

(Listing 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 n'importe laquelle de vos applications qui nécessitent le chiffrement du stockage de données. Tout d'abord, assurez-vous d'avoir une valeur pour votre clé et votre vecteur d'initialisation, puis appelez l'une des méthodes de chiffrement ou de déchiffrement de vos données avant de les stocker. Le listing 3 et Le listing 4 contiennent un exemple d'application simple de ces classes en utilisant. Nous créons une activité avec 3 boutons Crypter, décrypter, supprimer; 1 EditText pour la saisie de données; 1 TextView pour la sortie des données.

(Listing 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. 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>
27
Plo_Koon

Si la base de données est petite, vous pouvez gagner une petite quantité de sécurité en déchiffrant le fichier entier vers un emplacement temporaire (pas sur la carte SD), puis en rechiffrant 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 provoque un problème pour les clauses WHERE et ORDER BY. Si les champs chiffrés doivent être indexés pour la recherche d'équivalence, vous pouvez stocker un hachage cryptographique du champ et le rechercher. Mais cela n'aide pas avec les recherches de gamme ou les commandes.

Si vous voulez devenir plus amateur, vous pouvez vous plonger dans le NDK Android Android) et pirater du cryptage en code C pour 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? Vous pourriez être mieux avec quelque chose comme un fichier qui contient un objet sérialisé chiffré.

13
Mark Borgerding

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, wxSQLite (un joli codec gratuit est inclus dans le package)
  • SQLCipher inclut désormais le support pour Android
3
NuSkooler

http://sqlite-crypt.com/ peut vous aider à créer une base de données cryptée, même si je ne l'ai jamais utilisée sur Android semble être possible avec le code source.

0
Diego Torres Milano