Шифрование БД под управлением Firebird 3.0

в 15:53, , рубрики: firebird, Firebird/Interbase, информационная безопасность, конфиденциальность данных, криптография, шифрование данных

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

Шифрование БД под управлением Firebird 3.0 - 1

В апреле 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%FirebirdFirebird_3_0.

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

TESTDB = C:TESTAPPDBTESTDB.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%FirebirdFirebird_3_0isql -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
ParamsDatabase TESTDB
ParamsUserName SYSDBA
ParamsPassword masterkey
ParamsProtocol ipTCPIP
ParamsServer 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%FirebirdFirebird_3_0firebird” –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:TESTAPPCiFbEnc_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:TESTAPPCiFbEnc_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;

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

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

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

Автор: cipher-bis

Источник

Поделиться

* - обязательные к заполнению поля