1、介紹 SQLCipher是一個在SQLite基礎之上進行擴充的開源資料庫,它主要是在SQLite的基礎之上增加了資料加密功能,如果我們在項目中使用它來存儲資料的話,就可以大大提高程式的安全性。SQLCipher支援很多種不同的平台,這裡僅介紹Android中SQLCipher的用法。SQLCipher官網參見 https://www.zetetic.net/sqlcipher/ 。 網上的很多資料,大都說的是用sqlcipher加密,并通過密碼來打開資料庫及後續的增删改查操作等。但這些例子都是建立帶密碼的資料庫,而非對已有的資料庫進行加密和解密。對于已有的未加密的資料庫,顯然有極大的不便。另外一些資料,是通過sqlcipher的指令模式直接改密碼,本人未做嘗試,暫不做評論。基于此,才有了如下的項目。
2、利用AndroidStudio建立項目,并以gradle的方式将SQLCipher導入到我們的項目 在app級别的build.gradle中添加如下代碼: dependencies { compile 'net.zetetic:android-database-sqlcipher:[email protected]' }
然後,編譯項目即可。
查詢最新版本的SQLCipher,可參見如下網址 https://www.zetetic.net/sqlcipher/sqlcipher-for-android/。
3、項目隻有MainActivity.java和activity_main.xml兩個檔案:
- 布局檔案activity_main.xml代碼如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context="com.wjk.sqlciphertest.MainActivity"> <Button android:id="@+id/bt_encry" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="加密" /> <Button android:id="@+id/bt_decry" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="解密" /> </LinearLayout>
布局檔案中包括加密和解密兩個按鈕。
- 接下來在MainActivity.java中編寫加密和解密方法。主要用到SQLiteDatabase.rawExecSQL()和sqlcipher_export()兩個方法。先上代碼:
package com.***.sqlciphertest; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import net.sqlcipher.database.SQLiteDatabase; import java.io.File; public class MainActivity extends AppCompatActivity { private final String SDcardPath = "/mnt/sdcard/"; private Button mEncryptButton; private Button mDecryptButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SQLiteDatabase.loadLibs(this);//引用SQLiteDatabase的方法之前必須先添加這句代碼 mEncryptButton = (Button) findViewById(R.id.bt_encry); mDecryptButton = (Button) findViewById(R.id.bt_decry); mEncryptButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { encrypt("encryptedtest.db","test.db","1234"); } }); mDecryptButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { decrypt("encryptedtest.db","decryptedtest.db","1234"); } }); } /** * 加密資料庫 * @param encryptedName 加密後的資料庫名稱 * @param decryptedName 要加密的資料庫名稱 * @param key 密碼 */ private void encrypt(String encryptedName,String decryptedName,String key) { try { File databaseFile = getDatabasePath(SDcardPath + decryptedName); SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, "", null);//打開要加密的資料庫 /*String passwordString = "1234"; //隻能對已加密的資料庫修改密碼,且無法直接修改為“”或null的密碼 database.changePassword(passwordString.toCharArray());*/ File encrypteddatabaseFile = getDatabasePath(SDcardPath + encryptedName);//建立加密後的資料庫檔案 //deleteDatabase(SDcardPath + encryptedName); //連接配接到加密後的資料庫,并設定密碼 database.rawExecSQL(String.format("ATTACH DATABASE '%s' as "+ encryptedName.split("\\.")[0] +" KEY '"+ key +"';", encrypteddatabaseFile.getAbsolutePath())); //輸出要加密的資料庫表和資料到加密後的資料庫檔案中 database.rawExecSQL("SELECT sqlcipher_export('"+ encryptedName.split("\\.")[0] +"');"); //斷開同加密後的資料庫的連接配接 database.rawExecSQL("DETACH DATABASE "+ encryptedName.split("\\.")[0] +";"); //打開加密後的資料庫,測試資料庫是否加密成功 SQLiteDatabase encrypteddatabase = SQLiteDatabase.openOrCreateDatabase(encrypteddatabaseFile, key, null); //encrypteddatabase.setVersion(database.getVersion()); encrypteddatabase.close();//關閉資料庫 database.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 解密資料庫 * @param encryptedName 要解密的資料庫名稱 * @param decryptedName 解密後的資料庫名稱 * @param key 密碼 */ private void decrypt(String encryptedName,String decryptedName,String key) { try { File databaseFile = getDatabasePath(SDcardPath + encryptedName); SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, key, null); File decrypteddatabaseFile = getDatabasePath(SDcardPath + decryptedName); //deleteDatabase(SDcardPath + decryptedName); //連接配接到解密後的資料庫,并設定密碼為空 database.rawExecSQL(String.format("ATTACH DATABASE '%s' as "+ decryptedName.split("\\.")[0] +" KEY '';", decrypteddatabaseFile.getAbsolutePath())); database.rawExecSQL("SELECT sqlcipher_export('"+ decryptedName.split("\\.")[0] +"');"); database.rawExecSQL("DETACH DATABASE "+ decryptedName.split("\\.")[0] +";"); SQLiteDatabase decrypteddatabase = SQLiteDatabase.openOrCreateDatabase(decrypteddatabaseFile, "", null); //decrypteddatabase.setVersion(database.getVersion()); decrypteddatabase.close(); database.close(); } catch (Exception e) { e.printStackTrace(); } } }
代碼中已經做了詳盡的注釋,而且代碼也很簡單。主要是參考sqlcipher/sqlcipher-android-tests,網址見 https://github.com/sqlcipher/sqlcipher-android-tests 。
如果資料庫是沒有密碼的,加密後,再打開資料庫,則會提示file is encrypted or is not a database。再解密後,即可正常打開資料庫。
不論是建立的資料庫,還是已有的加密或沒加密過的資料庫,并且對更新資料庫的資料都會帶來極大的友善。
4、參考文獻 SQLCipher官網: https://www.zetetic.net/sqlcipher/sqlcipher-for-android/ sqlcipher/sqlcipher-android-tests: https://github.com/sqlcipher/sqlcipher-android-tests。
源碼下載下傳