標籤:
我們都知道,Android系統內建了SQLite資料庫,並且提供了一整套的API用於對資料庫進行增刪改查操作。資料庫儲存是我們經常會使用到的一種儲存方式,相信大多數朋友對它的使用方法都已經比較熟悉了吧。在Android中,我們既可以使用原生的SQL語句來對資料進行操作,也可以使用Android API提供的CRUD方法來對資料庫進行操作,兩種方式各有特點,選擇使用哪一種就全憑個人喜好了。
不過,使用SQLite來儲存資料卻存在著一個問題。因為大多數的Android手機都是Root過的,而Root過的手機都可以進入到/data/data/<package_name>/databases目錄下面,在這裡就可以查看到資料庫中儲存的所有資料。如果是一般的資料還好,但是當涉及到一些帳號密碼,或者聊天內容的時候,我們的程式就會面臨嚴重的安全性漏洞隱患。那麼今天,就讓我們一起研究一下如何藉助SQLCipher來解決這個安全性問題。
SQLCipher是一個在SQLite基礎之上進行擴充的開來源資料庫,它主要是在SQLite的基礎之上增加了資料加密功能,如果我們在項目中使用它來儲存資料的話,就可以大大提高程式的安全性。SQLCipher支援很多種不同的平台,這裡我們要學習的自然是Android中SQLCipher的用法了。
下面我們就開始吧,首先要把Android項目所依賴的SQLCipher工具包下載下來,是:
https://s3.amazonaws.com/sqlcipher/SQLCipher+for+Android+v2.2.2.zip
接著解壓這個工具包,會看到裡面有assets和libs這兩個目錄,稍後需要將這兩個目錄中的內容添加到Android項目當中。那麼現在我們就來建立一個Android項目,項目名就叫SQLCipherTest。
觀察SQLCipherTest的項目結構,發現裡面也分別有一個assets目錄和一個libs目錄,那麼現在就可以把SQLCipher工具包中這兩個目錄裡的內容複寫過來。並不需要複製全部檔案,選擇必要的檔案進行複製就可以了,完成以後項目結構圖如下所示,圖中顯示的檔案都是必要的。
到這裡準備工作就全部完成了,接下來我們開始編寫代碼。首先建立一個MyDatabaseHelper繼承自SQLiteOpenHelper,注意這裡使用的並不是Android API中的SQLiteOpenHelper,而是net.sqlcipher.database包下的SQLiteOpenHelper,代碼如下所示:
import android.content.Context; import net.sqlcipher.database.SQLiteDatabase; import net.sqlcipher.database.SQLiteDatabase.CursorFactory; import net.sqlcipher.database.SQLiteOpenHelper; public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_TABLE = "create table Book(name text, pages integer)"; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) { } }
除了引入的包不一樣了,其它的用法和傳統的SQLiteOpenHelper都是完全相同的。可以看到,我們在onCreate()方法中建立了一張Book表,Book表裡有name和pages這兩個列。
接著,開啟或建立activity_main.xml作為程式的主布局檔案,代碼如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/add_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="添加資料" /> <Button android:id="@+id/query_data" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="查詢資料" /> </LinearLayout>
這裡只是簡單地放置了兩個按鈕,分別用於添加和查詢資料。接下來開啟或建立MainActivity作為程式主Activity,代碼如下所示:
public class MainActivity extends Activity { private SQLiteDatabase db; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SQLiteDatabase.loadLibs(this); MyDatabaseHelper dbHelper = new MyDatabaseHelper(this, "demo.db", null, 1); db = dbHelper.getWritableDatabase("secret_key"); Button addData = (Button) findViewById(R.id.add_data); Button queryData = (Button) findViewById(R.id.query_data); addData.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { ContentValues values = new ContentValues(); values.put("name", "達芬奇密碼"); values.put("pages", 566); db.insert("Book", null, values); } }); queryData.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Cursor cursor = db.query("Book", null, null, null, null, null, null); if (cursor != null) { while (cursor.moveToNext()) { String name = cursor.getString(cursor.getColumnIndex("name")); int pages = cursor.getInt(cursor.getColumnIndex("pages")); Log.d("TAG", "book name is " + name); Log.d("TAG", "book pages is " + pages); } } cursor.close(); } }); } }
可以看到,在onCreate()方法中首先調用了SQLiteDatabase的loadLibs()靜態方法將SQLCipher所依賴的so庫載入進來,注意這裡使用的是net.sqlcipher.database包下的SQLiteDatabase。然後我們建立了MyDatabaseHelper的執行個體,並調用getWritableDatabase()方法去擷取SQLiteDatabase對象。這裡在調用getWritableDatabase()方法的時候傳入了一個字串參數,它就是SQLCipher所依賴的key,在對資料庫進行加解密的時候SQLCipher都將使用這裡指定的key。
在添加資料按鈕的點擊事件裡面,我們通過ContentValues構建了一條資料,然後調用SQLiteDatabase的insert()方法將這條資料插入到Book表中。
在查詢資料按鈕的點擊事件裡面,我們調用SQLiteDatabase的query()方法來查詢Book表中的資料,查詢到的結果會存放在Cursor對象中,注意這裡使用的是net.sqlcipher包下的Cursor。然後對Cursor對象進行遍曆,並將查詢到的結果列印出來。
現在運行一下程式,先點擊添加資料按鈕,再點擊查詢資料按鈕,剛剛添加的那條資料就應該在控制台裡列印出來了。
有沒有感覺到使用SQLCipher提供的API和使用Android原生的資料庫API,操作起來幾乎是一模一樣的。沒錯,SQLCipher對Android SDK中所有與資料庫相關的API都製作了一份鏡像,使得開發人員可以像操作普遍的資料庫檔案一樣來操作SQLCipher,而所有的資料加解密操作,SQLCipher都在背後幫我們處理好了。
話說寫到這裡,我們都一直還沒體驗一下SQLCipher加密後的效果呢,現在就來看一看吧,首先通過命令列的方式來訪問demo.db這個資料庫檔案:
adb shell cd /data/data/com.example.sqlciphertest/databases sqlite3 -line demo.db .table
嘗試查看demo.db中的所有表,結果返回如所示:
可以看出,當執行.table命令的時候被拒絕了,原因是資料庫檔案已加密。
除了使用命令列的方式,我們還可以嘗試使用Root Explorer來開啟資料庫檔案,結果如所示:
意料之中,果然開啟失敗了。這就足以說明,目前資料庫中的資料是非常安全的,只有在應用程式裡通過SQLCipher提供的API才可以訪問到資料庫裡的資料,使用其它的方式都無法擷取其資料。
需要提醒的一點是,項目中引入了SQLCipher之後,會讓你的程式體積驟然增加,打成APK後大概會變大好幾M,是更側重於檔案大小,還是更側重於程式安全,你應該根據具體的需求做出合適的判斷。
【轉】使用SQLCipher進行加解密