Szyfrowanie bazy danych Androida

79

Android używa bazy danych SQLite do przechowywania danych, muszę zaszyfrować bazę danych SQLite, jak to zrobić? Rozumiem, że dane aplikacji są prywatne. Jednak muszę jawnie zaszyfrować bazę danych SQLite, z której korzysta moja aplikacja.

user121196
źródło

Odpowiedzi:

68

SQLCipher to rozszerzenie SQLite, które zapewnia przezroczyste 256-bitowe szyfrowanie AES plików bazy danych.

Wcześniejszy sqlcipher, czyli pełne szyfrowanie bazy danych Open Source dla SQLite, nie był dostępny dla Androida. Ale teraz jest dostępna w wersji alfa na platformę Android. Programiści zaktualizowali standardową aplikację na Androida „Notepadbot”, aby używać SQLCipher.

Jest to obecnie zdecydowanie najlepsza i najprostsza opcja.

vaichidrewar
źródło
2
SQLCIpher dla Androida jest teraz częścią oficjalnego projektu SQLCipher
Nazwa wyświetlana
1
Informacje o licencji są dostępne na stronie github github.com/sqlcipher/android-database-sqlcipher/blob/master/…
vaichidrewar
2
@vaichidrewar Przekonasz się, że ten konkretny plik licencji dotyczy tylko części wsparcia dla Androida, istnieją dodatkowe pliki licencji dla rzeczy SQLCIPHER ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ), a także Rzeczy IBM ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ).
Hamid,
1
Dla prostego przykładu na SQLCipher w Androidzie, tutaj jest link myownandroid.blogspot.in/2013/09/sqlcipher-in-android.html
jrhamza
SQLCipher spowolnić działanie aplikacji wszelkie rozwiązanie tego problemu @vaichidrewar ??
Arsh Kaushal
28

Bazy danych są szyfrowane, aby temu zapobiec INDIRECT ATTACKS. Ten termin i klasy: KeyManager.java , Crypto.java pochodzą z książki Sherana Gunasekera Android Apps Security . Polecam całą tę książkę do przeczytania.

INDIRECT ATTACKSsą tak nazwane, ponieważ wirus nie atakuje bezpośrednio twojej aplikacji. Zamiast tego idzie po systemie operacyjnym Android. Celem jest skopiowanie wszystkich baz danych SQLite w nadziei, że autor wirusa będzie mógł skopiować wszelkie przechowywane w nich poufne informacje. Gdyby jednak dodać kolejną warstwę ochrony, wszystkie dane, które autor wirusa zobaczyłby, to zniekształcone dane. Zbudujmy bibliotekę kryptograficzną, której będziemy mogli ponownie wykorzystać we wszystkich naszych aplikacjach. Zacznijmy od stworzenia krótkiego zestawu specyfikacji:

  • Wykorzystuje algorytmy symetryczne: Nasza biblioteka będzie używać algorytmu symetrycznego lub szyfru blokowego do szyfrowania i odszyfrowywania naszych danych. Zdecydujemy się na AES, chociaż powinniśmy być w stanie to zmienić w późniejszym terminie.

  • Używa stałego klucza: Musimy mieć możliwość dołączenia klucza, który możemy przechowywać na urządzeniu, który będzie używany do szyfrowania i odszyfrowywania danych.

  • Klucz przechowywany na urządzeniu: klucz będzie znajdował się na urządzeniu. Chociaż jest to zagrożenie dla naszej aplikacji z punktu widzenia ataków bezpośrednich, powinno wystarczyć do ochrony nas przed atakami pośrednimi.

Zacznijmy od naszego modułu zarządzania kluczami (patrz Listing 1 ). Ponieważ planujemy użyć stałego klucza, nie będziemy musieli generować losowego, jak to robiliśmy w poprzednich przykładach. Dlatego KeyManager wykona następujące zadania:

  1. Zaakceptuj klucz jako parametr ( setId(byte[] data)metoda)
  2. Zaakceptuj wektor inicjalizacyjny jako parametr ( setIv(byte[] data) metoda)
  3. Przechowuj klucz w pliku w magazynie wewnętrznym
  4. Pobierz klucz z pliku w magazynie wewnętrznym ( getId(byte[] data) metoda)
  5. Pobierz IV z pliku w magazynie wewnętrznym ( getIv(byte[] data) metoda)

(Listing 1. Moduł 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());
         }
     }
}

Następnie wykonujemy moduł Crypto (patrz Listing 2 ). Ten moduł zajmuje się szyfrowaniem i deszyfrowaniem. Dodaliśmy metodę armorEncrypt()i armorDecrypt()do modułu, aby ułatwić konwersję danych tablicy bajtów na dane Base64 do wydrukowania i odwrotnie. Użyjemy algorytmu AES z trybem szyfrowania Cipher Block Chaining (CBC) i dopełnieniem PKCS # 5 .

(Listing 2. Moduł kryptograficzny 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)));
               }
}

Możesz dołączyć te dwa pliki do dowolnej aplikacji, która wymaga szyfrowania danych. Najpierw upewnij się, że masz wartość klucza i wektora inicjalizacji, a następnie wywołaj dowolną z metod szyfrowania lub deszyfrowania danych przed ich zapisaniem. Listing 3 i Listing 4 zawierają prosty przykład aplikacji tych klas przy użyciu. Tworzymy działanie z 3 przyciskami Szyfruj, odszyfruj, usuń; 1 EditText do wprowadzania danych; 1 TextView do wyprowadzania danych.

(Listing 3. Przykład. 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. Przykład. 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>
Plo_Koon
źródło
8
jeśli klucz jest przechowywany w urządzeniu, jakie są korzyści z szyfrowania, szyfrowania danych za pomocą tego klucza?
minhaz
Jak ustawić i pobrać klucz z innego pliku… czy możesz podać działający przykład? uzyskiwanie NPE podczas odczytu (pliku)
Gaju Kollur
13

Jeśli baza danych będzie mała, możesz uzyskać niewielką ochronę, odszyfrowując cały plik do tymczasowej lokalizacji (nie na karcie SD), a następnie ponownie zaszyfruj po zamknięciu. Problemy: przedwczesna śmierć aplikacji, obraz ducha na nośniku.

Nieco lepsze rozwiązanie do szyfrowania pól danych. Powoduje to problem z klauzulami WHERE i ORDER BY. Jeśli zaszyfrowane pola wymagają indeksowania w celu wyszukiwania równoważności, możesz zapisać kryptograficzny skrót pola i wyszukać go. Ale to nie pomaga w wyszukiwaniu zakresów lub porządkowaniu.

Jeśli chcesz stać się bardziej wyrafinowanym, możesz zagłębić się w Android NDK i zhakować trochę kryptowalut do kodu C dla SQLite.

Biorąc pod uwagę wszystkie te problemy i częściowe rozwiązania, czy na pewno potrzebujesz bazy danych SQL do aplikacji? Lepiej byłoby, gdybyś miał coś takiego jak plik, który zawiera zaszyfrowany obiekt serializowany.

Mark Borgerding
źródło
3

Z pewnością możesz mieć zaszyfrowaną bazę danych SQLite na Androida. Nie możesz tego jednak zrobić, jeśli nie masz klas dostarczonych przez Google.

Kilka alternatyw:

  • Skompiluj własny SQLite za pośrednictwem NDK i dołącz kodek szyfrujący z, na przykład, wxSQLite3 (ładny darmowy kodek jest zawarty w pakiecie)
  • SQLCipher zawiera teraz obsługę systemu Android
NuSkooler
źródło
1

http://sqlite-crypt.com/ może pomóc w utworzeniu zaszyfrowanej bazy danych, chociaż nigdy nie używałem jej na Androidzie, wydaje się, że jest to możliwe z kodem źródłowym.

Diego Torres Milano
źródło