1. 程式人生 > >利用SQLCipher加解密資料庫(包括加解密已有的資料庫)

利用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:3.5.4@aar' }
然後,編譯專案即可。 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();
            }
        }
    }
如果資料庫是沒有密碼的,加密後,再開啟資料庫,則會提示file is encrypted or is not a database。再解密後,即可正常開啟資料庫。 不論是新建的資料庫,還是已有的加密或沒加密過的資料庫,並且對更新資料庫的資料都會帶來極大的方便。 4、參考文獻 原始碼下載