Шифрование базы данных 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/… ).
— 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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.