Android-Datenbankverschlüsselung


79

Android verwendet die SQLite-Datenbank zum Speichern von Daten. Ich muss die SQLite-Datenbank verschlüsseln. Wie kann das gemacht werden? Ich verstehe, dass Anwendungsdaten privat sind. Ich muss jedoch die von meiner App verwendete SQLite-Datenbank explizit verschlüsseln.

Antworten:


68

SQLCipher ist eine SQLite-Erweiterung, die eine transparente 256-Bit-AES-Verschlüsselung von Datenbankdateien bietet.

Frühere SQL-Verschlüsselung, die Open Source Full Database Encryption für SQLite ist, war für Android nicht verfügbar. Aber jetzt ist es als Alpha-Release für Android-Plattform verfügbar. Entwickler haben die Standard-Android-Anwendung 'Notepadbot' aktualisiert, um SQLCipher zu verwenden.

Dies ist definitiv die beste und einfachste Option.


2
SQLCIpher für Android ist jetzt Teil des offiziellen SQLCipher-Projekts: sqlcipher.net/sqlcipher-for-android
Anzeigename

1
Lizenzinformationen sind auf der Github-Seite github.com/sqlcipher/android-database-sqlcipher/blob/master/…
vaichidrewar

2
@vaichidrewar Sie werden feststellen, dass diese bestimmte Lizenzdatei nur für den Android-Support-Teil gilt. Es gibt zusätzliche Lizenzdateien für das SQLCIPHER-Material ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ) sowie für das IBM Zeug ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ).
Hamid

1
Für ein einfaches Beispiel für SQLCipher in Android ist hier der Link myownandroid.blogspot.in/2013/09/sqlcipher-in-android.html
jrhamza

SQLCipher verlangsamen die Anwendung jede Lösung für das @vaichidrewar?
Arsh Kaushal

28

Datenbanken werden verschlüsselt, um dies zu verhindern INDIRECT ATTACKS. Dieser Begriff und die Klassen: KeyManager.java , Crypto.java stammen aus dem Buch Android Apps Security von Sheran Gunasekera . Ich empfehle das ganze Buch zum Lesen.

INDIRECT ATTACKSsind so benannt, weil der Virus nicht direkt nach Ihrer Anwendung geht. Stattdessen geht es nach dem Android-Betriebssystem. Ziel ist es, alle SQLite-Datenbanken in der Hoffnung zu kopieren, dass der Virenautor alle dort gespeicherten vertraulichen Informationen kopieren kann. Wenn Sie jedoch eine weitere Schutzschicht hinzugefügt hätten, würde der Virenautor nur verstümmelte Daten sehen. Erstellen wir eine kryptografische Bibliothek, die wir in allen unseren Anwendungen wiederverwenden können. Beginnen wir mit der Erstellung eines kurzen Satzes von Spezifikationen:

  • Verwendet symmetrische Algorithmen: Unsere Bibliothek verwendet einen symmetrischen Algorithmus oder eine Blockverschlüsselung, um unsere Daten zu verschlüsseln und zu entschlüsseln. Wir werden uns für AES entscheiden, obwohl wir dies zu einem späteren Zeitpunkt ändern können sollten.

  • Verwendet einen festen Schlüssel: Wir müssen in der Lage sein, einen Schlüssel einzuschließen, den wir auf dem Gerät speichern können, der zum Ver- und Entschlüsseln von Daten verwendet wird.

  • Auf dem Gerät gespeicherter Schlüssel: Der Schlüssel befindet sich auf dem Gerät. Dies ist zwar ein Risiko für unsere Anwendung aus Sicht direkter Angriffe, sollte jedoch ausreichen, um uns vor indirekten Angriffen zu schützen.

Beginnen wir mit unserem Schlüsselverwaltungsmodul (siehe Listing 1 ). Da wir einen festen Schlüssel verwenden möchten, müssen wir nicht wie in den vorherigen Beispielen einen zufälligen Schlüssel generieren. Der KeyManager führt daher folgende Aufgaben aus:

  1. Akzeptieren Sie einen Schlüssel als Parameter (die setId(byte[] data)Methode)
  2. Akzeptieren Sie einen Initialisierungsvektor als Parameter (die setIv(byte[] data) Methode)
  3. Speichern Sie den Schlüssel in einer Datei im internen Speicher
  4. Rufen Sie den Schlüssel aus einer Datei im internen Speicher ab (die getId(byte[] data) Methode)
  5. Rufen Sie die IV aus einer Datei im internen Speicher ab (die getIv(byte[] data) Methode)

(Listing 1. Das KeyManager-Modul 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());
         }
     }
}

Als nächstes machen wir das Crypto- Modul (siehe Listing 2 ). Dieses Modul kümmert sich um die Ver- und Entschlüsselung. Wir haben dem Modul eine armorEncrypt()und- armorDecrypt()Methode hinzugefügt , um die Konvertierung der Byte-Array-Daten in druckbare Base64- Daten und umgekehrt zu vereinfachen . Wir werden den AES- Algorithmus mit CBC-Verschlüsselungsmodus (Cipher Block Chaining) und PKCS # 5-Padding verwenden .

(Listing 2. Das Kryptografiemodul 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)));
               }
}

Sie können diese beiden Dateien in jede Ihrer Anwendungen aufnehmen, für die die Verschlüsselung des Datenspeichers erforderlich ist. Stellen Sie zunächst sicher, dass Sie einen Wert für Ihren Schlüssel und Ihren Initialisierungsvektor haben, und rufen Sie dann eine der Verschlüsselungs- oder Entschlüsselungsmethoden für Ihre Daten auf, bevor Sie sie speichern. Listing 3 und Listing 4 enthalten ein einfaches App-Beispiel für diese Klassen. Wir erstellen eine Aktivität mit 3 Schaltflächen Verschlüsseln, Entschlüsseln, Löschen. 1 EditText für die Dateneingabe; 1 TextView für die Datenausgabe.

(Listing 3. Ein Beispiel. 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. Ein Beispiel. 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
Wenn der Schlüssel im Gerät gespeichert ist, welche Vorteile bietet das Verschlüsseln und Verschlüsseln von Daten mit diesem Schlüssel?
Minhaz

Wie setze ich den Schlüssel und erhalte ihn aus einer anderen Datei? Kannst du ein funktionierendes Beispiel geben? NPE beim Lesen (Datei) bekommen
Gaju Kollur

13

Wenn die Datenbank klein ist, können Sie ein wenig Sicherheit gewinnen, indem Sie die gesamte Datei an einem temporären Speicherort (nicht auf einer SD-Karte) entschlüsseln und nach dem Schließen erneut verschlüsseln. Probleme: vorzeitiger App-Tod, Geisterbild auf Medien.

Eine etwas bessere Lösung zum Verschlüsseln der Datenfelder. Dies verursacht ein Problem für die WHERE- und ORDER BY-Klauseln. Wenn die verschlüsselten Felder für die Äquivalenzsuche indiziert werden müssen, können Sie einen kryptografischen Hash des Felds speichern und danach suchen. Dies hilft jedoch nicht bei der Bereichssuche oder -bestellung.

Wenn Sie schicker werden möchten, können Sie sich mit dem Android NDK befassen und Krypto in C-Code für SQLite hacken.

Sind Sie angesichts all dieser Probleme und Teillösungen sicher, dass Sie wirklich eine SQL-Datenbank für die Anwendung benötigen? Mit einer Datei, die ein verschlüsseltes serialisiertes Objekt enthält, sind Sie möglicherweise besser dran.


3

Sie können sicherlich eine verschlüsselte SQLite-Datenbank unter Android haben. Sie können dies jedoch nicht mit den von Google bereitgestellten Standardklassen tun.

Ein paar Alternativen:

  • Kompilieren Sie Ihre eigene SQLite über das NDK und fügen Sie den Verschlüsselungscodec von beispielsweise wxSQLite3 hinzu (ein netter kostenloser Codec ist im Paket enthalten).
  • SQLCipher jetzt auch Android

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.