2010-02-04 249 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=..."