Шифрування бази даних Android


79

Android використовує базу даних SQLite для зберігання даних, мені потрібно зашифрувати базу даних SQLite, як це можна зробити? Я розумію, що дані програми є приватними. Однак мені потрібно явно зашифрувати базу даних SQLite, яку використовує моя програма.

Відповіді:


68

SQLCipher - це розширення SQLite, яке забезпечує прозоре 256-бітове AES-шифрування файлів баз даних.

Раніше sqlcipher, який є відкритим кодом повного шифрування баз даних для SQLite, був недоступний для android. Але зараз він доступний як альфа-версія для платформи Android. Розробники оновили стандартний додаток для Android "Notepadbot", щоб використовувати SQLCipher.

Тож це, безумовно, найкращий і найпростіший варіант на сьогодні.


2
SQLCIpher для Android тепер є частиною офіційного проекту SQLCipher
Відображуване ім'я

1
Інформація про ліцензію доступна на сторінці github github.com/sqlcipher/android-database-sqlcipher/blob/master/…
vaichidrewar

2
@vaichidrewar Ви виявите, що конкретний файл ліцензії застосовується лише до частини підтримки Android, є додаткові файли ліцензії для матеріалів SQLCIPHER ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ), а також Речі IBM ( github.com/sqlcipher/android-database-sqlcipher/blob/master/… ).
Хамід

1
Для простого прикладу на SQLCipher в android, ось посилання myownandroid.blogspot.in/2013/09/sqlcipher-in-android.html
jrhamza

SQLCipher сповільнює програму будь-яким рішенням для цього @vaichidrewar ??
Арш Каушаль

28

Бази даних шифруються з метою запобігання INDIRECT ATTACKS. Цей термін і класи: KeyManager.java , Crypto.java взяті з Шерана Гунасекера книга Android Apps безпеки . Всю цю книгу я рекомендую прочитати.

INDIRECT ATTACKSназвані так, оскільки вірус не йде безпосередньо після вашої програми. Натомість він йде за ОС Android. Метою є копіювання всіх баз даних SQLite в надії, що автор вірусу може скопіювати будь-яку конфіденційну інформацію, що там зберігається. Якби ви додали ще один рівень захисту, тоді автор вірусів побачив би лише спотворені дані. Давайте створимо криптографічну бібліотеку, яку ми зможемо використовувати у всіх наших додатках. Почнемо зі створення короткого набору специфікацій:

  • Використовує симетричні алгоритми: наша бібліотека використовуватиме симетричний алгоритм або блоковий шифр для шифрування та дешифрування наших даних. Ми зупинимося на AES, хоча ми могли б змінити це пізніше.

  • Використовує фіксований ключ: ми повинні мати можливість включити ключ, який ми можемо зберегти на пристрої, який буде використовуватися для шифрування та дешифрування даних.

  • Ключ, що зберігається на пристрої: ключ зберігатиметься на пристрої. Хоча це ризик для нашої програми з точки зору прямих атак, цього має бути достатньо для захисту нас від непрямих атак.

Почнемо з нашого модуля управління ключами (див. Лістинг 1 ). Оскільки ми плануємо використовувати фіксований ключ, нам не потрібно буде генерувати випадковий ключ, як це було в минулих прикладах. KeyManager виконуватиме такі завдання:

  1. Прийміть ключ як параметр ( setId(byte[] data) метод)
  2. Прийміть вектор ініціалізації як параметр ( setIv(byte[] data) метод)
  3. Зберігайте ключ у файлі у внутрішньому сховищі
  4. Отримати ключ із файлу у внутрішньому сховищі ( getId(byte[] data) метод)
  5. Отримати IV з файлу у внутрішньому сховищі ( getIv(byte[] data) метод)

(Лістинг 1. Модуль 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());
         }
     }
}

Далі ми робимо модуль Crypto (див. Лістинг 2 ). Цей модуль піклується про шифрування та дешифрування. Ми додали модуль armorEncrypt()і armorDecrypt()метод до модуля, щоб полегшити перетворення даних байтових масивів у дані Base64 для друку та навпаки. Ми будемо використовувати алгоритм AES з режимом шифрування шифрування блоків шифру (CBC) та відступом PKCS # 5 .

(Лістинг 2. Криптографічний модуль 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)));
               }
}

Ви можете включити ці два файли до будь-якої програми, для якої потрібно шифрувати дані. По-перше, переконайтеся, що у вас є значення для вашого ключа та вектора ініціалізації, а потім викликайте будь-який із методів шифрування або розшифрування ваших даних, перш ніж зберігати їх. Лістинг 3 та Лістинг 4 містять просто додаток для прикладу використання цих класів. Ми створюємо Діяльність за допомогою 3 кнопок Шифрувати, Розшифрувати, Видалити; 1 EditText для введення даних; 1 TextView для виведення даних.

(Лістинг 3. Приклад. 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 ");
            }
        });

    }

}

(Лістинг 4. Приклад. 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
якщо ключ зберігається у пристрої, які переваги шифрування, шифрування даних за допомогою цього ключа?
minhaz

Як встановити та отримати ключ з іншого файлу .. чи можна навести робочий приклад ?? отримання NPE при читанні (файл)
Гаджу Коллур,

13

Якщо база даних буде невеликою, тоді ви зможете отримати невелику кількість захисту, розшифрувавши весь файл у тимчасовому розташуванні (не на SD-карті), а потім повторно зашифрувавши, коли ви його закрили. Проблеми: передчасна смерть додатків, зображення привидів на носіях.

Трохи краще рішення для шифрування полів даних. Це спричиняє проблему для речень WHERE та ORDER BY. Якщо зашифровані поля потрібно проіндексувати для пошуку за еквівалентністю, тоді ви можете зберігати криптографічний хеш поля та шукати це. Але це не допомагає при пошуку діапазону або впорядкуванні.

Якщо ви хочете поцікавитись, ви можете заглибитися в Android NDK і зламати трохи криптографічного коду на C для SQLite.

Розглядаючи всі ці проблеми та часткові рішення, ви впевнені, що вам дійсно потрібна база даних SQL для програми? Можливо, вам буде краще щось на зразок файлу, який містить зашифрований серіалізований об’єкт.


3

Ви, безсумнівно, можете мати зашифровану базу даних SQLite на Android. Однак це неможливо зробити із нестандартних класів, наданих Google.

Пара альтернатив:

  • Скомпілюйте свій власний SQLite через NDK і включіть кодек шифрування, наприклад, від wxSQLite3 (хороший безкоштовний кодек включений в пакет)
  • Тепер SQLCipher включає підтримку Android

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.