天天看點

利用SQLCipher加解密資料庫(包括加解密已有的資料庫)

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。

源碼下載下傳