Шифрование базы данных Android - PullRequest
75 голосов
/ 05 февраля 2010

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

Ответы [ 5 ]

66 голосов
/ 07 сентября 2011

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

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

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

26 голосов
/ 24 ноября 2013

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

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

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

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

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

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

  1. Принять ключ в качестве параметра (метод setId(byte[] data))
  2. Принять вектор инициализации в качестве параметра (setIv(byte[] data) метод)
  3. Хранить ключ внутри файла во внутреннем хранилище
  4. Получить ключ из файла во внутреннем хранилище (getId(byte[] data) метод)
  5. Получить IV из файла во внутреннем хранилище (getIv(byte[] data) метод) * +1048 *

(Листинг 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 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 содержат простой пример использования этих классов в приложении. Мы создаем действие с 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>
13 голосов
/ 20 марта 2010

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

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

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

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

3 голосов
/ 10 ноября 2010

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...