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

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

Ответы

Ответ 1

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

Ранее sqlcipher, который является Open Source Full Database Encryption для SQLite, недоступен для Android. Но теперь он доступен как альфа-релиз для платформы Android. Разработчики обновили стандартное приложение Android Notepadbot для использования SQLCipher.

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

Ответ 2

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

INDIRECT ATTACKS названы так, потому что вирус не подходит сразу после вашего приложения. Вместо этого он идет после ОС Android. Цель состоит в том, чтобы скопировать все базы данных SQLite в надежде, что автор вирусов сможет скопировать любую конфиденциальную информацию, хранящуюся там. Однако, если вы добавили еще один уровень защиты, все автор вирусов увидит искаженные данные. Давайте построим криптографическую библиотеку, которую мы можем повторно использовать во всех наших приложениях. Давайте начнем с создания краткого набора спецификаций:

  • Использует симметричные алгоритмы: наша библиотека будет использовать симметричный алгоритм, или блочный шифр для шифрования и расшифровки наших данных. Мы остановимся на AES, хотя мы должны иметь возможность изменить это позже.

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

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

Давайте начнем с нашего модуля управления ключами (см. Листинг 1). Поскольку мы планируем использовать фиксированный ключ, нам не нужно генерировать случайное, как в прошлых примерах. Поэтому KeyManager выполняет следующие задачи:

  • Принять ключ в качестве параметра (метод setId(byte[] data))
  • Принять вектор инициализации в качестве параметра (setIv(byte[] data) метод)
  • Храните ключ внутри файла во внутреннем хранилище
  • Извлеките ключ из файла во внутреннем хранилище (getId(byte[] data) метод)
  • Извлеките 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 padding.

(листинг 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>

Ответ 3

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

Несколько лучшее решение для шифрования полей данных. Это вызывает проблему для предложений WHERE и ORDER BY. Если зашифрованные поля нужно индексировать для поиска эквивалентности, вы можете сохранить криптографический хэш поля и выполнить поиск. Но это не помогает в поиске диапазона или заказе.

Если вы хотите стать более привлекательным, вы можете вникать в Android NDK и взломать крипто в C-код для SQLite.

Учитывая все эти проблемы и частичные решения, вы уверены, что вам действительно нужна база данных SQL для приложения? Возможно, вам будет лучше с чем-то вроде файла, который содержит зашифрованный сериализованный объект.

Ответ 4

У вас наверняка есть зашифрованная база данных SQLite на Android. Однако вы не можете делать это с помощью классов, предоставленных Google.

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

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

Ответ 5

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

Ответ 6

litereplica поддерживает encryption используя шифр ChaCha.

Чача почти в 3 раза быстрее, чем AES на портативных устройствах на базе ARMv7.

привязки для Android.

Чтобы создать и открыть зашифрованную базу данных, мы используем URI следующим образом:

"file:/path/to/file.db?cipher=...&key=..."