2010-02-04 260 views
65

Android使用SQLite数据库存储数据,我需要加密SQLite数据库,这怎么办呢?我了解,应用程序数据是私人的。不过,我需要明确地加密我的应用程序正在使用的SQLite数据库。Android数据库加密

+0

我已加密的所有值甚至主键和解密。它慢,但它的工作。什么是最佳方案。 – 2011-02-24 09:40:33

回答

0

http://sqlite-crypt.com/可能会帮助你创建一个加密的数据库,虽然我从来没有用过它在android上似乎有可能与源代码。

13

如果数据库很小,那么可以通过将整个文件解密到一个临时位置(不在SD卡上),然后在关闭它时重新进行加密,从而获得一小部分安全性。问题:应用程序过早死亡,媒体上的鬼影。

稍微好一点的解决方案来加密数据字段。这会导致WHERE和ORDER BY子句出现问题。如果加密字段需要为等价搜索建立索引,则可以存储该字段的加密哈希并搜索该字段。但是这对范围搜索或排序没有帮助。

如果你想更有趣,你可以深入研究Android NDK,并将一些加密技术转换为SQLite的C代码。

考虑到所有这些问题和部分解决方案,你确定你确实需要一个SQL数据库的应用程序?您可能会更喜欢使用包含加密序列化对象的文件。

3

你当然可以在Android上有一个加密的SQLite数据库。但是,Google提供的课程不能用Google提供的课程来完成。

几个选择:

  • 编译通过NDK自己的SQLite和包括例如加密的编解码器,wxSQLite3(包括在包一个不错的免费编解码器)
  • SQLCipher现在包括支持Android
60

SQLCipher是一种SQLite扩展,它提供对数据库文件的透明256位AES加密。

以前的sqlcipher是SQLite的开源完整数据库加密不适用于android。但现在它可以作为Android平台的alpha版本。 开发人员已将标准android应用程序'Notepadbot'更新为使用SQLCipher。

所以这绝对是目前最好的和最简单的选项。

+1

SQLCIpher for Android现在是SQLCipher官方项目的一部分:http://sqlcipher.net/sqlcipher-for-android/ – 2012-09-06 07:37:48

+1

是免费使用还是必须购买许可证?我找不到准确的信息..:S – Ewoks 2012-11-16 12:40:46

+1

许可证信息可在github页面上获得https://github.com/sqlcipher/android-database-sqlcipher/blob/master/LICENSE – vaichidrewar 2012-11-16 15:56:18

22

数据库被加密以防止INDIRECT ATTACKS。 这个词和类:KeyManager.javaCrypto.javaSheran GunasekeraAndroid Apps Security拍摄。我推荐所有这本书阅读。

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()); 
     } 
    } 
} 

接下来,我们做加密模块(参见列表2)。该模块负责加密和解密。我们已经将armorEncrypt()armorDecrypt()方法添加到模块中,以便将字节数组数据转换为可打印的Base64数据,反之亦然。我们将使用AES算法与Cipher Block Chaining (CBC) encryption modePKCS#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> 
+7

,加密有什么好处使用该密钥加密数据? – minhaz 2016-03-06 05:07:57

0

litereplica使用支持encryption ChaCha密码。

Chacha与基于ARMv7的便携式设备上的AES几乎相比3 times faster

Android版本有bindings

要创建并打开我们使用URI这样一个加密的数据库:

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