Шифрование базы данных Android

79

Android использует базу данных SQLite для хранения данных, мне нужно зашифровать базу данных SQLite, как это можно сделать? Я понимаю, что данные приложения являются конфиденциальными. Однако мне нужно явно зашифровать базу данных SQLite, которую использует мое приложение.

user121196
источник

Ответы:

68

SQLCipher - это расширение SQLite, которое обеспечивает прозрачное 256-битное шифрование AES файлов базы данных.

Ранее sqlcipher, представляющий собой полное шифрование базы данных с открытым исходным кодом для SQLite, был недоступен для Android. Но теперь он доступен в виде альфа-версии для платформы Android. Разработчики обновили стандартное приложение для Android Notepadbot, чтобы использовать SQLCipher.

Так что это определенно лучший и самый простой вариант на данный момент.

Vaichidrewar
источник
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/… ).
Hamid
1
Для простого примера SQLCipher в android, вот ссылка myownandroid.blogspot.in/2013/09/sqlcipher-in-android.html
jrhamza
SQLCipher замедлить работу приложения любое решение для этого @vaichidrewar ??
Арш Каушал
28

Базы данных зашифрованы для предотвращения INDIRECT ATTACKS. Этот термин и классы: KeyManager.java , Crypto.java взяты из книги Шерана Гунасекера « Безопасность приложений Android» . Всю эту книгу рекомендую к прочтению.

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 с режимом шифрования Cipher Block Chaining (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 приведен простой пример использования этих классов в приложении. Мы создаем Activity с 3 кнопками Encrypt, Decrypt, Delete; 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
NuSkooler
источник
1

http://sqlite-crypt.com/ может помочь вам создать зашифрованную базу данных, хотя я никогда не использовал ее на android, похоже, это возможно с исходным кодом.

Диего Торрес Милано
источник