Delphi开发Android使用wxsqlite3为数据库加密

华涵意
2023-12-01

Delphi开发Android使用wxsqlite3为数据库加密

  近段时间对Delphi开发Android程序进行了一些尝试,发现了一些问题,也试着去解决这些问题,特将这些解决方法记录下来,以便自己和他人翻阅。由于本人接触Android程序时间有限,许多问题都是边开写代码边在网上找一些资料,我这篇文章也是集成一些网上的办法,结合自己的研究(本文所用开发工具:Delphi 10.2)。下面详细讲解:

步骤一:将wxsqlite3编译成SO动态链接库

  wxsqlite3是开源的sqlite3加密模块,比较有名,对于我们来说,能不花钱的方法才是最好的,在Android下使用wxsqlite3必须将其编译成安卓支持的SO文件。网上有很多关于安卓下sqlite3加密的方法,但基本上是Java代码,而且需要Java开发工具,对于我这种delphier来说,Java虽接触过,仍是小白一个,只好另寻他法。最终找到用ndk-build编译so的方法,Delphi开发安卓必须要ndk,参照网上的方法反复折腾,始终是不得其法 。经过一番尝试,还是让我这种小白大致明白了问题所在,ndk-build编译so必须把所有C语言源码放在jni目录之下,然后直接调用命令即可。对于使用Java开发的人来说,是再简单不过了,让我们这种delphier情何以堪。

  其中还有两个关键点:一是Android.mk文件的编写。ndk-build是依赖这个文件进行编译的,一并放在jni目录下即可。结构如下:

LOCAL_PATH := $(call my-dir)

#清理变量定义
include $(CLEAR_VARS)

#模块名称
LOCAL_MODULE := wxsqlite3

#库文件名称
LOCAL_MODULE_FILENAME := libwxsqlite3

#源文件
LOCAL_SRC_FILES := sqlite3secure.c

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)

#头文件目录u
LOCAL_C_INCLUDES := $(LOCAL_PATH)

#构建动态库
include $(BUILD_SHARED_LIBRARY)
以上已经进行了注释。这是最简单的应用,对于我们编译wxsqlite3足够了,网上还有许多复杂的方法,我认为简单就是好,so文件仅是用于Delphi来调用的。

  第二个关键点是使用ndk-build命令编译C源码。Android.mk写好了,且C源码全部放在jni目录下后,我们返加jni的上一级目录,在这个目录下编写一个cmd文件执行命令。为了看到编译的过程,用记事本写入“cmd.exe”,存为cmd扩展名的文件。执行后进入dos界面,输入:%NDKPATH%\ndk-build回车即可,其中“%NDKPATH%”是你的NDK-build所在目录,我的是在“D:\Program Files\Embarcadero\Studio\19.0\PlatformSDKs\android-ndk-r9c”这里,如果目录有空格要将“Program Files”改为“Progra~1”才行。经常用ndk-build的话,可以将路径添加到环境变量里。这样编译成功后,so文件就放在“..\libs\armeabi”下,注意,这样编译的是不带加密功能的。如何带加密功能,网上的办法是在C源码里添加“#define SQLITE_HAS_CODEC”,经我反复测试根本不行。最后打开C源码研究了一下,才知道这里有一个坑,我下载的是最新的源码,较以前的源码可能有较大改动,其实应该是在sqlite3secure.c文件顶端添加“#define SQLITE_HAS_CODEC 1”即可,默认的是AES128加密,应该也够用了。如果要用AES256,需要将codec.h文件里的第46行“#define CODEC_TYPE CODEC_TYPE_AES128”改成AES256就行了,或是在这个文件的顶端加入“#define CODEC_TYPE CODEC_TYPE_AES256”也行。顺便提一句,Android.mk文件也可以直接定义预编译指令,就不需要改C源码,有兴趣的同志可以在网上搜一下,我没有试过。

步骤二:Delphi调用有密码功能的so文件

  对于Delphi来说调用so文件与调用DLL没有什么区别。新建一个FMX应用,project ->deployment->然后点击增加按钮,选择中libwxsqlite.so动态链接库,remote path 填写 assets\internal\,就是将so文件打包到Android里。 我们调用这个库是不能用FireDAC的,FirdDAC不支持Android下的加密,我们也没有办法将Android原装的libsqlite.so替换掉,所以必须写代码来支持加密版的sqlite3,与调用DLL没有什么区别, 注意:Delphi 10.2安卓开发已经不支持静态调用DLL了,必须动态调用,究竟 是Delphi版本的问题还是SDK/NDK版本的问题没有研究过。在网上打到SimpleSQLite却是不支持Android的,只能在Win下使用。本着DIY精神,费了好大功夫修改了源码,将其修改的关键表述如下:
  一是所有PanisCha类型在Android下均不支持,替换为 MarshaledAString。
  二是所有静态引用使用更换为动态调用。这里做个示例,以后遇到同样的问题可以这样修改(修改好的源码文末有链接下载):
原来是:
function(filename: PChar; var db: TSQLiteDB): integer; cdecl;external......
修改为:
type
TSQLite3_Open16=function(filename: PChar; var db: TSQLiteDB): integer; cdecl;
然后再声明:var  sqlite3_open16:TSQLite3_Open16;
sqlite3有一堆需要动态调用的函数,不可能每个都写个动态调用过程,我是这样处理的,先定义一个全局变量 var hlib:Cardinal=0;再写三个个函数:
procedure LoadLib;//用来加载so动态链接库
begin
  {$IFDEF ANDROID}
  SQLiteDLL:=GetFilesDir+'/libwxsqlite.so';//注意这里,声明的时候不要用const SQLiteDLL='....',定义一个全局变量
  {$ENDIF}
     hlib:=LoadLibrary(PChar(SQLiteDLL));
end;
function GetProc(const Name: string):Pointer;//动态调用
begin
  if hlib<>0 then
  begin
     Result:=GetProcAddress(hLib, PChar(Name));
  end;
end;
procedure InitFunctions;//每个动态调用的函数获得指针
begin
  sqlite3_open16 := GetProc('sqlite3_open16');
  //.........................................
end;
在第三个函数里,把所有需要调用的都写进去就可以了。这里就是修改的核心。
SQLiteTable.pas修改原则上也是如此,SQLite3.pas原来的代码是没有加密功能的,需要添加进去:
TSQLite3_key=function(pDb: TSQLiteDB;          // Database handle
    pKey: PChar;        // Database PassWord (UTF-8)
    nKey: Integer           // Database sizeofpassword
  ): integer;cdecl;
Tsqlite3_rekey =function (
    pDb: TSQLiteDB;          // Database handle
    pKey: PChar;        // Database PassWord (UTF-8)
    nKey: Integer           // Database sizeofpassword
  ): integer;  c
因为修改幅度较大,这里仅说一下更改的关键地方。
最后提醒一点的是:sqite3.dll与libwxsqlite.so尽量要使用同版本的源码,否则有可能手机与电脑上的数据库不能通用。我已经编译好了128位加密功能的so和DLL文件,共享给大家。如果需要256位的自行编译吧。

下载地址,需要资源分2,原来是可以定义为零的,本想免费给大家,请大家多多担待:
  

 类似资料: