简介
SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL 数据库引擎。SQLite 源代码不受版权限制。
这里再说明下,SQLite3是SQLite的第三个主要版本,避免大家突然看到Sqlite3不知道什么意思。
Sqlite3的加/解密
首先你得先把Sqlite3的库给下载到本地,项目里引用该库文件,然后就能调用它的Api啦。
加密说明
-
sqlite3_key
是输入密钥,如果数据库已加密必须先执行此函数并输入正确密钥才能进行操作,如果数据库没有加密,执行此函数后进行数据库操作反而会出现 “此数据库已加密或不是一个数据库文件” 的错误。 -
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey)
,db 是指定数据库,pKey 是密钥,nKey 是密钥长度。例:sqlite3_key( db, "abc", 3)
。 -
sqlite3_rekey
是变更密钥或给没有加密的数据库添加密钥或清空密钥,变更密钥或清空密钥前必须先正确执行sqlite3_key
。在正确执行sqlite3_rekey
之后在sqlite3_close
关闭数据库之前可以正常操作数据库,不需要再执行sqlite3_key
。 -
int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey)
,参数同上。 -
清空密钥为
sqlite3_rekey(db, NULL, 0)
。
解密说明
-
在调用 sqlite3_open() 函数打开数据库后,要调用 sqlite3_key() 函数为数据库设置密码。
-
如果数据库之前有密码,则调用 sqlite3_key() 函数设置正确密码才能正常工作。
-
如果一个数据库之前没有密码,且已经有数据,则不能再为其设置密码。
-
如果要修改密码,则需要在第一步操作后,调用 sqlite3_rekey() 函数设置新的密码。
-
设置了密码的 SQLite 数据库,无法使用第三方工具直接打开,必须要输入密码。
Windows下如何利用BAT加解密Sqlite
这里需要下载sqlcipher工具,网上可以找一个叫sqlcipher-3.0.1-windows的就可以了。
基本原理说明
现在有三个数据库【数据库名称随意】 origin.db(这个是源数据库,也就是你想要加密的数据库。) encrypted.db (这个加密后的数据库) plaintext.db(这个是解密后的数据库) 加密:把源数据库里面的所有数据,复制到一个已经加密的数据库encrypted.db中,encrypted.db这个数据库是运行命令的时候生成的,并且是加密的。 解密:把加过密的数据库encrypted.db里面所有的数据拷贝到plaintext.db数据库中,plaintext.db是运行命令的时候生成的,并且没有密码。
具体操作
-
解压下载好的工具。 bin目录里面的结构:
-
打开命令行【win+R ->cmd】 使用命令打开工具所在的文件夹,我的文件夹是:E:\360安全浏览器下载\AAA\sqlcipher- windows\bin
输入命令1:sqlcipher-shell64.exe origin.db 【完成之后按Enter键】 输入命令2:ATTACH DATABASE ‘encrypted.db’ AS encrypted KEY ‘thisiskey’; 【Enter】 输入命令3:SELECT sqlcipher_export(‘encrypted’); 【Enter】 输入命令4:DETACH DATABASE encrypted; 【Enter】
上述命令完成之后,就完成了一个加密的过程。接下来解释一下上面的命令: 命令1,使用工具sqlcipher-shell64.exe ,空格之后,后面紧跟着的是你要操作的源数据库。 命令2,新建一个数据库文件encrypted.db,并打开连接。这里encrypted.db是一个加密的db文件
encrypted
是该数据库的别名,thiskey
是新建的这个数据库文件的密码 命令3,调用了工具的一个接口,该接口的作用就是把上面操作的源数据库origin.db里面的所有数据拷贝到encryted.db当中。 命令4,断开连接解密:把加密的数据库encryted.db里面所有的数据拷贝到未加密的数据库plaintext.db中。 使用window命令行打开到bin目录文件夹 输入命令1,sqlcipher-shell64.exe encryted.db 【Enter】 输入命令2,PRAGMA key = ‘thisiskey’; 输入命令3,ATTACH DATABASE ‘plaintext.db’ AS plaintext KEY ‘’; 输入命令4,SELECT sqlcipher_export(‘plaintext’); 输入命令5,DETACH DATABASE plaintext;
命令解释: 命令1,要操作加密的数据库 命令2,输入加密数据库的密码 命令3,新建了一个数据库plaintext.db, 密码是空【两个单引号中间是空】 命令4,拷贝加密数据库中所有的数据到plaintext.db中 命令5,断开连接 上述命令完成之后就完成了一个解密的过程。 使用sqlite打开plaintext.db发现里面数据和源数据库中的文件一致
DB创建自动化
using Core.Utils;
using GameSystem.Database;
using GameSystem.Proxy;
using GameSystem.VO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
public class CreateDB : EditorWindow
{
private static EditorWindow window;
private string dbName =
#if UNITY_ANDROID
"assets_android.db";
#elif UNITY_IOS
"assets_ios.db";
#endif
private string dbKey = Key; //"76A8EF9C-2052-4D3A-8A30-DCAACC5BEFF6"
private bool need_encrypt = true;
[MenuItem("本地化工具/Window/CreateDB")]
public static void CreateLocalDB()
{
window = GetWindow<CreateDB>("创建本地数据库");
window.Show();
}
void OnGUI()
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("请输入DB的名字:", GUILayout.Width(120f));
dbName = GUILayout.TextField(dbName);
if (GUILayout.Button("浏览"))
{
string fileFullPath = UnityEditor.EditorUtility.OpenFilePanel("Open File Dialog", UnityEngine.Application.streamingAssetsPath, "db");
if (!string.IsNullOrEmpty(fileFullPath))
{
string[] pathArray = fileFullPath.Split('/');
dbName = pathArray[pathArray.Length - 1];
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("请设置DB的密码:", GUILayout.Width(120f));
dbKey = GUILayout.TextField(dbKey);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("是否需要加密数据库:", GUILayout.Width(120f));
need_encrypt = EditorGUILayout.Toggle(need_encrypt);
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("创建"))
{
window.Close();
CreateDBProcess();
}
}
private void CreateDBProcess()
{
var db = new SqliteDatabase(dbName, dbName, Application.streamingAssetsPath);
db.Open(); //此时路径下自动会产生一个db文件
CreateTable<LocalAssetsMasterVO>(db, typeof(LocalAssetsMasterVO).Name);
//db.Close();
int count = 1;
foreach (var item in LoadProxy.LocalAbFileNamesList)
{
EditorUtility.DisplayProgressBar("数据库生成", string.Format("生成中:{0}/{1}", count, LoadProxy.LocalAbFileNamesList.Count), (float)count++ / LoadProxy.LocalAbFileNamesList.Count);
var query = new StringBuilder("replace into " + typeof(LocalAssetsMasterVO).Name + " (");
query.Append(CreateFieldNames(typeof(LocalAssetsMasterVO)));
query.Append(") values ");
var valuesStr = CreateFieldValues(new LocalAssetsMasterVO() { code = item, version = "default_version" });
db.ExecuteNonQuery(query.ToString() + valuesStr);
}
EditorUtility.ClearProgressBar();
if (need_encrypt)
{
string batRootPath = Path.Combine(Application.dataPath, @"Editor\Tools\CreateDB\bat");
string batName = "encrypt_db.bat";
string batPath = Path.Combine(batRootPath, batName);
//可传递参数给批处理
System.Diagnostics.Process p = new System.Diagnostics.Process();
//第二个参数为传入的参数,string类型以空格分隔各个参数
//把需要加密的数据库名字去除后缀后的名字传递进去
System.Diagnostics.ProcessStartInfo pi = new System.Diagnostics.ProcessStartInfo(
batPath,
string.Format("{0} {1}", Path.GetFileNameWithoutExtension(dbName), dbKey)
);
pi.UseShellExecute = false;
pi.RedirectStandardOutput = true;
p.StartInfo = pi;
p.Start();
p.WaitForExit();
}
AssetDatabase.Refresh();
EditorUtility.DisplayDialog("", "创建完毕", "OK");
}
-------------------------------------------------------------------------------------
#region SupportProprites
private static string streamingPath = Application.streamingAssetsPath;
private static GameDatabase m_database;
private Dictionary<Type, MasterCache> m_masterMaps;
private static MasterProxy m_masterProxy;
static readonly byte[] xorkey = { 0x1E, 0x9F, 0x83, 0xEA, 0xB0, 0xE2, 0xDB, 0x95 };
static readonly byte[] src = {
0x29,0xA9,0xC2,0xD2,0xF5,0xA4,0xE2,0xD6,0x33,0xAD,0xB3,0xDF,0x82,0xCF,0xEF,0xD1,
0x2D,0xDE,0xAE,0xD2,0xF1,0xD1,0xEB,0xB8,0x5A,0xDC,0xC2,0xAB,0xF3,0xA1,0xEE,0xD7,
0x5B,0xD9,0xC5,0xDC
};
public static string Key { get { return Encoding.ASCII.GetString(Utils.XorBytes(src, xorkey)); } }
static private readonly string DefaultApiDomainName = "game-ntp-dev";
private const string SharedCacheFileName = "assets_android";
public static string ApiDomainName
{
get
{
if (PlayerPrefs.HasKey("ApiDomainName"))
{
return PlayerPrefs.GetString("ApiDomainName");
}
PlayerPrefs.SetString("ApiDomainName", DefaultApiDomainName);
return DefaultApiDomainName;
}
}
#endregion
#region SupportMethod
private static string CreateFieldNames(Type type)
{
int index = 0;
var fields = type.GetFields();
var ret = new StringBuilder();
foreach (var field in fields)
{
if (index != 0)
{
ret.Append(", ");
}
ret.Append(field.Name);
index++;
}
return ret.ToString();
}
private static string CreateFieldValues<T>(T record)
{
int index = 0;
var values = new StringBuilder("(");
var fields = typeof(T).GetFields();
foreach (var field in fields)
{
if (index != 0)
{
values.Append(", ");
}
if (field.FieldType == typeof(string))
{
var str = (string)field.GetValue(record);
values.Append(String.Format("'{0}'", WWW.EscapeURL(str)));
}
else if (
field.FieldType == typeof(float)
|| field.FieldType == typeof(double)
|| field.FieldType == typeof(int)
|| field.FieldType == typeof(long)
)
{
values.Append(field.GetValue(record));
}
else if (field.FieldType == typeof(bool))
{
var value = Convert.ToInt32(field.GetValue(record));
values.Append(value);
}
else
{
int value = (int)field.GetValue(record);
values.Append(value);
}
index++;
}
values.Append(")");
return values.ToString();
}
private static void CreateTable<T>(SqliteDatabase db, string tableName)
{
var type = typeof(T);
var query = new StringBuilder("create table if not exists " + tableName + " (");
int index = 0;
Debug.Assert(type.GetFields().Length > 0);
foreach (var field in type.GetFields())
{
if (index != 0)
{
query.Append(", ");
}
string t = "";
if (field.FieldType == typeof(string))
{
t = "TEXT";
}
else if (field.FieldType == typeof(float))
{
t = "REAL";
}
else
{
t = "INTEGER";
}
query.Append(field.Name + " " + t);
index++;
}
query.Append(")");
db.ExecuteNonQuery(query.ToString());
}
#endregion
}
BAT脚本
cd /D %~dp0
set db_name=%1
set db_key=%2
(
echo ATTACH DATABASE '%db_name%' AS encrypted KEY '%db_key%';
echo SELECT sqlcipher_export^('encrypted'^);
)>encrypt_query.sql
sqlcipher-shell64.exe D:\Prince_Of_Tennis\ver_current_chs\dlive-client\Dlive\Assets\StreamingAssets\%db_name%.db < encrypt_query.sql
move D:\Prince_Of_Tennis\ver_current_chs\dlive-client\Dlive\Assets\Editor\Tools\CreateDB\bat\%db_name% D:\Prince_Of_Tennis\ver_current_chs\dlive-client\Dlive\Assets\StreamingAssets\
SQL语句文件,利用输入重定向跟在加密命令之后
ATTACH DATABASE 'test_333_444' AS encrypted KEY '789';
SELECT sqlcipher_export('encrypted');