[Из песочницы] Шифрование БД под управлением Firebird 3.0

Habrahabr

В современном информационном мире, информация играет значительную роль в жизни человека, общества и государства. Рост размера накапливаемых и обрабатываемых данных подымает вопросы об их хранении и обеспечении конфиденциальности. Уже существует немало технических решений и предложений для решения подобных задач. Среди них конечно же есть и системы управления базами данных (СУБД) которые поддерживают шифрование хранимых данных. Вот об одном из таких решений и пойдёт речь.

В апреле 2016 года вышла новая версия СУБД Firebird под номером 3. Из нововведений, среди прочего, появилось и немало механизмов защиты хранимых и передаваемых данных. Там есть и защита канала передачи данных, есть управление пользователями, а также есть шифрование самой БД, которое реализовано как прозрачное шифрование на уровне страниц данных. Реализуется это всё с помощью написания специальных расширений для Firebird. Можно конечно и самому разобраться и написать эти расширения, но почему бы не взять существующие. Тем более, что для написания, как минимум, нужно понимать в криптографии, иметь на вооружении знания какого-нибудь криптографического пакета и разобраться с новым С++ Firebird API. Можно воспользоваться существующим готовым решением, скачав по этой ссылке для платформы Windows x64. В бесплатном ознакомительном варианте оно ограничено используемым алгоритмом шифрования — поддерживается Triple DES (3DES). Но для защиты своей БД этого вполне достаточно. В состав пакета входит 4 библиотеки и файлы с описанием интерфейсов.
Наименование файла Описание
CiKeyHolder.dll расширение Firebird – хранитель секретного ключа
CiDbCrypt.dll расширение Firebird – шифрования данных
CiFbEnc_x86.dll модуль активации ключа шифрования для платформы 32 bit.
CiFbEnc_x86-64.dll модуль активации ключа шифрования для платформы 64 bit.
ICiFbEncActivator.h описание интерфейса модуля для С++
CI.ICiFbEncActivator.pas описание интерфейса модуля для Delphi
Как пример, можно написать приложение, которое использует некий справочник (условно с конфиденциальными данными), который как обновляемый доступен по ссылке в глобальной сети. Его может скачать кто угодно, но при этом расшифровать и работать с ним сможет только наше приложение.

Дабы не захламлять статью лишними деталями реализации подобной задачи, при которой необходимо обеспечить получение ключа и организации обновления файла БД, будем предполагать, что это есть, а ключ будет просто как переменная массив в программе и файл БД сами зашифруем, используя этот ключ.

В качестве платформы возьмём ОС Windows 10 x64, а в качестве инструментов реализации задачи будем использовать IDE Embarcadero RAD Studio (на данный момент была доступна версия 10.2 Tokyo — 30-дневная пробная), поскольку она содержит удобные компоненты для работы с БД Firebird и применяется часто для разработки прикладных решений (пробную версию можно скачать по ссылке) и СУБД Firebird 3.0.2 x64, которую можно скачать по ссылке https://www.firebirdsql.org/en/firebird-3-0-2 для своей платформы. Так как нам в режиме разработки не нужен постоянно выполняемый сервер, да и перезапуск его может понадобиться неоднократно, то во время инсталляции выбираем способ запуска сервера как приложения.

Установка Firebird
Пароль для SYSDBA делаем по старинке masterkey и снимаем галочку отвечающую за запуск сервера после установки – пока не нужно.

После того, как был проинсталлирован пакет Firebird, необходимо произвести его настройку. Если же планируется использование Firebird в Embedded режиме, то такие же настройки нужно будет проделать в директории, где будет находиться используемая библиотека клиент fbclient.dll. Базовые файлы настроек находятся в корневой директории установленного Firebird — %ProgramW6432%\Firebird\Firebird_3_0.

Пропишем псевдоним (алиас) БД в файле databases.conf, добавив строку с указанием полного пути к будущей БД:

TESTDB = C:\TESTAPP\DB\TESTDB.FDB
Для того, чтобы Firebird знал, где ему искать ключи, необходимо определить параметр KeyHolderPlugin. Его можно указать или конкретно для выше описанного псевдонима или в файле firebird.conf. Добавляем или изменяем параметр конфигурации файла firebird.conf, определяя плагин хранитель секретного ключа:
KeyHolderPlugin = CiKeyHolder
Где искать плагины Firebird узнаёт также из конфигурационных файлов. Изменим файл plugins.conf, добавив следующие строки:
Plugin = CiKeyHolder {
        # Прописываем путь к плагину хранителю
        Module = $(dir_plugins)/CiKeyHolder
}

Plugin = CiDbCrypt {
        # Прописываем путь к плагину шифрования
        Module = $(dir_plugins)/CiDbCrypt
}
Директория под псевдонимом $(dir_plugins) подразумевает директорию plugins в корневом каталоге Firebird. Копируем туда ранее скачанные плагины CiKeyHolder.dll и CiDbCrypt.dll.

Поскольку мы предполагаем, что БД справочник у нас уже есть, создадим её средствами пакета Firebird, а именно, используя приложение isql. Жмём Win + R и выполняем следующую команду:

%ProgramW6432%\Firebird\Firebird_3_0\isql -q -user SYSDBA -password masterkey
Или же как-то по-другому запускаем isql, например, используя программу Far.

Следующие команды создадут БД TESTDB, псевдоним которой ранее был прописан в настройках и выведут информацию о созданной БД:

SQL> create database 'TESTDB';
SQL> create table t_1 (i1 int, s1 varchar(15), s2 varchar(15));
SQL> insert into t_1 values (1,'value 1-1','value 1-2');
SQL> insert into t_1 values (2,'value 2-1','value 2-2');
SQL> commit;
SQL> select * from t_1;

          I1 S1              S2
============ =============== ===============
           1 value 1-1       value 1-2
           2 value 2-1       value 2-2

SQL> show db;
Database: TESTDB
        Owner: SYSDBA
PAGE_SIZE 8192
Number of DB pages allocated = 196
Number of DB pages used = 184
Number of DB pages free = 12
Sweep interval = 20000
Forced Writes are ON
Transaction - oldest = 4
Transaction - oldest active = 5
Transaction - oldest snapshot = 5
Transaction - Next = 9
ODS = 12.0
Database not encrypted
Default Character set: NONE
SQL> quit;
Как видно из полученной информации по команде show db;, созданная БД в данный момент не зашифрована — Database not encrypted.

Теперь всё настроено и можно приступить к написанию приложения.

Создаём новый проект VCL Forms Application в IDE Embarcadero RAD Studio. Сохраняем проект, например, как testapp в директории C:\TESTAPP.

Для начала мы можем просто связать наше приложение с БД, обеспечив подключение и отключение к ней. Наша тестовая БД имеет одну таблицу, данные которой можно вывести при подключении. Из визуального оформления достаточно пары кнопок, таблица и навигатор с управлением данными. Для доступа к БД Firebird воспользуемся компонентами FireDAC. Далее представлена таблица с компонентами и их настраиваемыми параметрами.

Компоненты, помещаемые на форму тестового приложения
Класс компонента Наименование компонента Параметр Значение параметра
TFDPhysFBDriverLink FDPhysFBDriverLink1 - -
TFDConnection FDConnection1 DriverName FB
LoginPrompt False
Params\Database TESTDB
Params\UserName SYSDBA
Params\Password masterkey
Params\Protocol ipTCPIP
Params\Server localhost
TFDGUIxWaitCursor FDGUIxWaitCursor1 - -
TFDTransaction FDTransaction1 - -
TFDTable FDTable1 TableName T_1
TDataSource DataSource1 DataSet FDTable1
TDBGrid DBGrid1 DataSource DataSource1
TDBNavigator DBNavigator1 DataSource DataSource1
TButton Button1 Caption Connect
TButton Button2 Caption Disconnect
В итоге форма может выглядеть приблизительно так.
Форма в дизайнере среды разработки
Пропишем обработку нажатия на кнопку “Connect”:
FDConnection1->Connected = true;
FDTable1->Active = true;
И для “Disconnect”:
FDTable1->Active = false;
FDConnection1->Connected = false;
В случае использования Delphi всё аналогично:
FDConnection1.Connected := True;
FDTable1.Active := True;

. . . 

FDConnection1.Connected := False;
FDTable1.Active := False;
Прежде чем подключиться к БД, необходимо запустить сервер. Для этого необходимо вызвать командную строку администратора:

Win + X → Командная строка (администратор)

И выполнить команду:

“%ProgramW6432%\Firebird\Firebird_3_0\firebird” –a

В трее появится значок запущенного сервера. Из контекстного меню вызванном над этим значком можно завершить его работу или посмотреть свойства запущенного сервера.

Теперь можно собрать и запустить приложение для проверки возможности подключения к БД. После подключения к БД в таблице отобразятся данные.

Успешное подключение к БД
Настало время подключить модуль активации ключа и управления шифрованием к нашему приложению. Для этого копируем файлы CiFbEnc_x86.dll и ICiFbEncActivator.h, CI.ICiFbEncActivator.pas в директорию C:\TESTAPP. Стоит обратить внимание, что поскольку по умолчанию в среде разработки создаётся проект под 32-bit Windows и триал версия среды разработки не позволяет изменить целевую платформу, приложению необходим именно файл-модуль CiFbEnc_x86.dll.

Подключим заголовочный файл с описанием интерфейса модуля активации к файлу приложения:

#include "ICiFbEncActivator.h"
Или файл Ci.ICiFbEncActivator.pas в случае Delphi.
uses
  . . . , CI.ICiFbEncActivator;
Добавим на форму ещё три кнопки: “Encrypt”, “Decrypt” и “Get State”.
Дополнительные кнопки для запуска операций модуля
Для начала рассмотрим обработчик кнопки “Get State”. Что следует из названия, это даст нам возможность получить текущее состояние БД.

Самое главное, — это получить доступ к интерфейсу модуля активации. Его можно получать как для каждой операции, так и глобально или агрегируя в некий объект. Чтобы не городить классы-обертки, будем предполагать, что получаем объект модуля активации при каждой операции. Ниже представлен один из возможных способов сделать это.

// C++ Builder

// Загружаем модуль
std::unique_ptr<HINSTANCE__, decltype(&::FreeLibrary)> mHandle(
        ::LoadLibraryEx(L"C:\\TESTAPP\\CiFbEnc_x86.dll",0,LOAD_WITH_ALTERED_SEARCH_PATH),
        &::FreeLibrary);

if (!mHandle)
{
        MessageBox(
                NULL,
                L"Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll",
                L"Загрузка модуля",
                MB_OK|MB_ICONERROR);

        return;
}

// Получаем активатор
typedef CALL_CONV int(__stdcall *CREATEFUNCPTR)(CI::ICiFbEncActivator**);
CREATEFUNCPTR GetActivator = 
(CREATEFUNCPTR)::GetProcAddress(mHandle.get(), "createCiFBEncActivator");

if (!GetActivator)
{
        MessageBox(
                NULL,
                L"Не удалось получить из модуля CiFbEnc_x86.dll процедуру" 
              "createCiFBEncActivator"
                 " - попробуйте посмотреть, что пишет tdump.",
                L"Загрузка модуля",
                MB_OK|MB_ICONERROR);

        return;
}

CI::ICiFbEncActivator* pActivator = NULL;
GetActivator(&pActivator);
if (!pActivator) { ShowMessage("ERROR GetActivator!"); return; }

// . . .
//
// обращения к объекту модуля активации
//
// . . .

// Уничтожаем экземпляр активатора
pActivator->Destroy();
pActivator = NULL;
// Delphi

var
pActivator : ICiFbEncActivator;
res : Integer;
CreateActivator: TActivatorFunction;
mHandle : HINST;
. . .
begin
// Загружаем модуль
mHandle := LoadLibraryEx(
PChar('C:\TESTAPP\CiFbEnc_x86.dll'), 0, LOAD_WITH_ALTERED_SEARCH_PATH);

if mHandle = 0 then
begin
     MessageBox(
        Application.Handle,
        'Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
      Exit;
end;

// Получаем активатор
CreateActivator := GetProcAddress(mHandle, 'createCiFBEncActivator');
if not Assigned(CreateActivator) then
begin
     MessageBox(
        Application.Handle,
        'Не удалось получить из модуля CiFbEnc_x86.dll процедуру createCiFBEncActivator'
        +
        ' - попробуйте посмотреть, что пишет tdump.',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);

      Exit;
end;

pActivator := nil;
res := CreateActivator(pActivator);
if not Assigned(pActivator) then begin ShowMessage('ERROR CreateActivator!'); Exit; end;

// . . .
//
// обращения к объекту модуля активации
//
// . . .
 

// Уничтожаем экземпляр активатора
pActivator.Destroy;
pActivator := nil;

FreeLibrary(mHandle);
end;
Далее будем предполагать, что для каждого из обработчиков нажатия на кнопку будет добавлен вышеуказанный код.

Теперь можно перейти и к получению состояния БД.

// C++ Builder

. . .

// Устанавливаем параметры подключения к БД
pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

// Посмотрим, что нам расскажет о БД сервис Firebird
char stat_buf[1024] = { 0 };
size_t bufsize = sizeof(stat_buf);

int res = pActivator->GetStateSVC(stat_buf, bufsize);
String sStatMsg = (String)stat_buf;
if (Err_OK == res)
{
        MessageBox(NULL, sStatMsg.c_str(), L"Статус БД", MB_OK|MB_ICONINFORMATION);
}
else
{
        String sErrMsg = L"ERROR GetStateSVC: " + sStatMsg;
        MessageBox(
                NULL,
                sErrMsg.c_str(),
                L"Статус БД",
                MB_OK|MB_ICONERROR);

}

. . .
// Delphi

var
. . .
stat_buf : array[0..1023] of AnsiChar;
bufsize : NativeUInt;

. . .

// Устанавливаем параметры подключения к БД
res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

// Посмотрим, что нам расскажет о БД сервис Firebird
bufsize := SizeOf(stat_buf);
ZeroMemory(@stat_buf, bufsize);
res := pActivator.GetStateSVC(stat_buf, bufsize);

if Err_OK = res then
begin
       MessageBox(Application.Handle, PChar(String(stat_buf)),
       'Статус БД', MB_OK OR MB_ICONINFORMATION);
end
else
begin
       MessageBox(Application.Handle, PChar(String(stat_buf)),
       'Ошибка', MB_OK OR MB_ICONERROR);
end;

. . .
В случае, если удалось подключиться к БД и получить информацию, мы увидим сообщение с детальной информацией о текущем состоянии.
Успешное получение текущего статуса БД
Как видно, БД всё ещё не зашифрована. Пора это исправить. Но для шифрования необходим ключ. И мы его определим как глобальную константу в рамках примера.
// C++ Builder

// От куда-то у нас есть ключ 192 бита
const uint8_t key[24] =
{
        0x06,0xDE,0x81,0xA1,0x30,0x55,0x1A,0xC9,
        0x9C,0xA3,0x42,0xA9,0xB6,0x0F,0x54,0xF0,
        0xB6,0xF9,0x70,0x18,0x85,0x04,0x83,0xBF
};
// Delphi

const
key : array [0..23] of Byte =
(
        $06,$DE,$81,$A1,$30,$55,$1A,$C9,
        $9C,$A3,$42,$A9,$B6,$0F,$54,$F0,
        $B6,$F9,$70,$18,$85,$04,$83,$BF
);
Кроме получения текущего состояния БД, все остальные операции, такие как зашифровать, расшифровать и получить доступ к зашифрованным данным, требуют активации секретного ключа. Для этого добавим после получения объекта модуля активации следующий код:
// C++ Builder

. . .

// Устанавливаем параметры подключения к БД
pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

// Устанавливаем ключ активатору
int res = pActivator->SetKey(&key, sizeof(key));
if (Err_OK != res) { ShowMessage("ERROR SetKey!"); pActivator->Destroy(); return; }

// Активируем доступ к ключу
res = pActivator->Activate();
if (Err_OK != res)
{
// Обрабатываем ошибку
        char errmsg[512] = {0};
        size_t esize = sizeof(errmsg);
        pActivator->GetFBStat(errmsg, esize);
        String sErrMsg = "ERROR Activate: " + String(errmsg);
        MessageBox(
                NULL,
                sErrMsg.w_str(),
                L"Активация доступа к ключу",
                MB_OK|MB_ICONERROR);

        pActivator->Destroy();
        return;
}

. . .
// Delphi

. . .

// Устанавливаем параметры подключения к БД
res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

// Устанавливаем ключ активатору
res := pActivator.SetKey(@key, Length(key));
if Err_OK <> res then begin ShowMessage('ERROR SetKey!'); pActivator.Destroy; Exit; end;

// Активируем доступ к ключу
res := pActivator.Activate;
if Err_OK <> res then
begin
bufsize := SizeOf(errmsg);
        ZeroMemory(@errmsg, bufsize);
        pActivator.GetFBStat(errmsg, bufsize);
        MessageBox(
                Application.Handle,
                PChar('ERROR Activate: ' + String(errmsg)),
                'Активация доступа к ключу',
                MB_OK OR MB_ICONERROR);

        pActivator.Destroy;
        Exit;
end;

. . .
Как видно, происходит передача ключа активатору и вызов функции активации. При этом модуль регистрирует функцию обратного вызова для доступа к ключу средствами расширения хранителя ключа. И это не зависит от режима работы Firebird. А безопасность передачи ключа гарантируется шифрованием на сессионных ключах. В вышеизложенном коде после вызова функции активации показан пример обработки возникновения возможной ошибки на уровне Firebird библиотеки. Подобным образом можно считывать текущий статус выполненной задачи для любой из операций с обращением к функционалу Firebird.

Теперь можем написать обработчики кнопок “Encrypt” и “Decrypt”. Если опустить проверку статуса выполненной задачи и предварительную активацию ключа, то достаточно только добавить вызов у объекта активации одноимённой функции:

// C++ Builder

// зашифровать
res = pActivator->Encrypt();

. . .

// расшифровать
res = pActivator->Decrypt();
// Delphi

// зашифровать
res := pActivator.Encrypt;

. . .

// расшифровать
res := pActivator.Decrypt;

В итоге мы получим идентичные обработчики за исключением одного вызова.

Теперь мы можем зашифровать БД. А вызов её статуса расскажет нам о том, что она уже зашифрована.

Статус зашифрованной БД
После этого, попытка выполнить подключение к БД не меняя обработчик кнопки “Connect” приведёт к ошибке.
Попытка подключения к зашифрованной БД без активации ключа
Последний шаг – это изменение обработчика кнопки подключения к БД. Туда нужно добавить получение объекта интерфейса модуля и вызов активации ключа. Ниже представлен полностью код обработки подключения к зашифрованной БД.
Для C++ Builder
// C++ Builder

void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Загружаем модуль
        std::unique_ptr<HINSTANCE__, decltype(&::FreeLibrary)> mHandle(
                ::LoadLibraryEx(L"C:\\TESTAPP\\CiFbEnc_x86.dll", 0,
LOAD_WITH_ALTERED_SEARCH_PATH),
                &::FreeLibrary);

        if (!mHandle)
        {
                MessageBox(
                        NULL,
                        L"Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll",
                        L"Загрузка модуля",
                        MB_OK|MB_ICONERROR);
                return;
        }

        // Получаем активатор
        typedef CALL_CONV int(__stdcall *CREATEFUNCPTR)(CI::ICiFbEncActivator**);
        CREATEFUNCPTR GetActivator = 
(CREATEFUNCPTR)::GetProcAddress(mHandle.get(), "createCiFBEncActivator");

        if (!GetActivator)
        {
                MessageBox(
                        NULL,
                        L"Не удалось получить из модуля CiFbEnc_x86.dll"
" процедуру createCiFBEncActivator"
                         " - попробуйте посмотреть, что пишет tdump.",
                        L"Загрузка модуля",
                        MB_OK|MB_ICONERROR);
                return;
        }

        CI::ICiFbEncActivator* pActivator = NULL;
        GetActivator(&pActivator);
        if (!pActivator) { ShowMessage("ERROR GetActivator!"); return; }

        // Устанавливаем параметры подключения к БД
        pActivator->SetDBAccess("localhost:TESTDB", "SYSDBA", "masterkey");

        // Устанавливаем ключ активатору
        int res = pActivator->SetKey(&key, sizeof(key));
        if (Err_OK != res) { ShowMessage("ERROR SetKey!"); pActivator->Destroy();return; }

        // Активируем доступ к ключу
        res = pActivator->Activate();
        if (Err_OK != res)
        {
                // Обрабатываем ошибку
                char errmsg[512] = {0};
                size_t esize = sizeof(errmsg);
                pActivator->GetFBStat(errmsg, esize);
                String sErrMsg = "ERROR Activate: " + String(errmsg);
                MessageBox(
                        NULL,
                        sErrMsg.w_str(),
                        L"Активация доступа к ключу",
                        MB_OK|MB_ICONERROR);
                pActivator->Destroy();
                return;
        }

        // Подключаемся к БД
        try
        {
                FDConnection1->Connected = true;
                FDTable1->Active = true;
        }
        catch(EFDDBEngineException &e)
        {
                String sErrMsg = L"Не удалось подключиться к БД. " + e.Message;
                MessageBox(
                        NULL,
                        sErrMsg.w_str(),
                        L"Подключение к БД",
                        MB_OK|MB_ICONERROR);
        }

        // Уничтожаем экземпляр активатора
        pActivator->Destroy();
        pActivator = NULL;
}
Для Delphi
// Delphi

procedure TForm2.Button1Click(Sender: TObject);
var
pActivator : ICiFbEncActivator;
res : Integer;
CreateActivator: TActivatorFunction;
mHandle : HINST;
errmsg : array[0..511] of AnsiChar;
bufsize : NativeUInt;
begin
  // Загружаем модуль
        mHandle := LoadLibraryEx(PChar('C:\TESTAPP\CiFbEnc_x86.dll'), 0,
 LOAD_WITH_ALTERED_SEARCH_PATH);

  if mHandle = 0 then
  begin
     MessageBox(
        Application.Handle,
        'Модуль CiFbEnc_x86.dll не найден или рядом с ним нет fbclient.dll',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
      Exit;
  end;

  // Получаем активатор
  CreateActivator := GetProcAddress(mHandle, 'createCiFBEncActivator');
  if not Assigned(CreateActivator) then
  begin
     MessageBox(
        Application.Handle,
        'Не удалось получить из модуля CiFbEnc_x86.dll процедуру createCiFBEncActivator'
 +
        ' - попробуйте посмотреть, что пишет tdump.',
        'Загрузка модуля',
        MB_OK OR MB_ICONERROR);
                  Exit;
  end;
  pActivator := nil;
  res := CreateActivator(pActivator);
  if not Assigned(pActivator) then begin ShowMessage('ERROR CreateActivator!');Exit; end;

  // Устанавливаем параметры подключения к БД
  res := pActivator.SetDBAccess('localhost:TESTDB', 'SYSDBA', 'masterkey');

  // Устанавливаем ключ активатору
  res := pActivator.SetKey(@key, Length(key));
  if Err_OK <> res then begin ShowMessage('ERROR SetKey!'); pActivator.Destroy;Exit; end;

  // Активируем доступ к ключу
  res := pActivator.Activate;
  if Err_OK <> res then
  begin
        bufsize := SizeOf(errmsg);
        ZeroMemory(@errmsg, bufsize);
        pActivator.GetFBStat(errmsg, bufsize);
        MessageBox(
                Application.Handle,
                PChar('ERROR Activate: ' + String(errmsg)),
                'Активация доступа к ключу',
                MB_OK OR MB_ICONERROR);

        pActivator.Destroy;
        Exit;
  end;

  // Подключаемся к БД
  try
        FDConnection1.Connected := True;
FDTable1.Active := True;
  except
        on E: EFDDBEngineException do begin
       MessageBox(
        Application.Handle,
        PChar('Не удалось подключиться к БД. ' + String(E.Message)),
        'Подключение к БД',
        MB_OK OR MB_ICONERROR);
    end;
  end;

  // Уничтожаем экземпляр активатора
  pActivator.Destroy;
  pActivator := nil;

  FreeLibrary(mHandle);
end;
Вот так вот достаточно просто можно работать с зашифрованной БД.

Полностью проект можно взять тут.

Демо-пакет — тут.