В современном информационном мире, информация играет значительную роль в жизни человека, общества и государства. Рост размера накапливаемых и обрабатываемых данных подымает вопросы об их хранении и обеспечении конфиденциальности. Уже существует немало технических решений и предложений для решения подобных задач. Среди них конечно же есть и системы управления базами данных (СУБД) которые поддерживают шифрование хранимых данных. Вот об одном из таких решений и пойдёт речь.
В апреле 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 для своей платформы. Так как нам в режиме разработки не нужен постоянно выполняемый сервер, да и перезапуск его может понадобиться неоднократно, то во время инсталляции выбираем способ запуска сервера как приложения.
Пароль для 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
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
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
Здравствуйте!
Хорошая статья. Но при практическом знакомстве возник вопрос о размере ключа (unsuccessful metadata update -ALTER DATABASE failed – Incorrectly key size. Cause of error: Ошибка вызова) при вызове res := pActivator.Encrypt; Возвращаемое значение res = -5 (Firebird вернул ошибку). Использую Firebird 32 bit, модуль CiFbEnc_x86.dll и плагины из папки х86. Подскажите пож. есть ли зависимость ключа от разрядности сервера?
С уважением! Заранее благодарен за ответ.
Здравствуйте! Почитал Dfie статью – огромное спасибо за нее, много ответов дала.
Подскажите: попытался повторить описанное в Вашей статье приложение но в Embedded
режиме работы сервера Firebird 3 (прилагаю ссылку на архив с полностью настроенным проектом и Embedded БД https://www.dropbox.com/s/f4zxqzg3a4ri35f/Demo_FB3Emb_Cipher.rar ) – практически все получается, почти все работает – в частности БД успешно шифрует и успешно расшифровывает, к не зашифрованной БД успешно коннектиться,
НО! не дает к зашифрованной БД подключиться – вылетает ошибка ‘[FireDAC][Phys][FB]The secret key was not activated!’ – я так догадываюсь надо перед коннектом к БД как то связать ранее активированный ключ с компонентой FDPhysFBDriverLink1 приложения?
Это как то решаемо при использовании именно Embedded Firebird 3?