OpenSSL -> LibSSL

Вопросы использования сторонних (не входящих в состав FPC и Lazarus) утилит и библиотек.

Модератор: Модераторы

OpenSSL -> LibSSL

Сообщение RuCode » 15.07.2014 19:39:36

Всем привет, интересует вопрос работы c RSA, по началу начал использовать openssl по средствам вызова fpsystem('/bin/openssl lalala'); Но по мере роста программы внезапно оказалось что сотня другая дисковых операций могут затянуть простое действие на несколько десятков секунд, что не приемлемо для юзаюилити, а учитывая, что я ещё и архивирую файлы тем же способом через tar, то естественно получить большую производительность возможно лишь через выполнение всех операций на лету, в памяти процесса, если с libtar я разобрался легко, то с openssl ну просто Ж*ПА... А именно:

Так и не удалось завести RSA в стандартном модуле, пришлось дергать ещё функции:
Код: Выделить всё
uses
  ...
OpenSSL, dynlibs;

type
  TPEM_write_bio_RSAPrivateKey = function(Pri: PBIO; KeyPair: PRSA;
    var1, var2, var3, var4, var5: pointer): integer; cdecl;
  TPEM_write_bio_RSAPublicKey = function(Pri: PBIO; KeyPair: PRSA): integer; cdecl;

...

var
  PEM_write_bio_RSAPrivateKey: TPEM_write_bio_RSAPrivateKey;
  PEM_write_bio_RSAPublicKey: TPEM_write_bio_RSAPublicKey;

implementation

{$R *.lfm}

procedure DoLoadOpenSSL;
var
  hlib: TLibHandle;
begin
  hlib := LoadLibrary(DLLSSLName + '.so');
  PEM_write_bio_RSAPrivateKey :=
    TPEM_write_bio_RSAPrivateKey(GetProcAddress(hlib, 'PEM_write_bio_RSAPrivateKey'));
  if PEM_write_bio_RSAPrivateKey = nil then
    ShowMessage('');
  PEM_write_bio_RSAPublicKey :=
    TPEM_write_bio_RSAPublicKey(GetProcAddress(hlib, 'PEM_write_bio_RSAPublicKey'));
  if PEM_write_bio_RSAPublicKey = nil then
    ShowMessage('');
end;

...

initialization
  DoLoadOpenSSL;

end.


Дальше пошло чуть проще, генерация ключей:
Код: Выделить всё
function GenRsaKeys(KeySize: integer; var PriKey: string; var PubKey: string): PRSA;
const
  PUB_EXP = 3;
var
  PriLen, PubLen: integer;
  KeyPair: PRSA;
  Pri:     PBIO;
  Pub:     PBIO;
begin
  KeyPair := RsaGenerateKey(KeySize, PUB_EXP, nil, nil);
  Pri     := BioNew(BioSMem);
  Pub     := BioNew(BioSMem);
  PEM_write_bio_RSAPrivateKey(pri, keypair, nil, nil, nil, nil, nil);
  PEM_write_bio_RSAPublicKey(pub, keypair);
  Prilen := BioCtrlPending(pri);
  Publen := BioCtrlPending(pub);
  SetLength(PriKey, PriLen + 1);
  SetLength(PubKey, PubLen + 1);
  BioRead(pri, PriKey, PriLen);
  BioRead(pub, PubKey, PubLen);
  BioFreeAll(pub);
  BioFreeAll(pri);
  Result := keypair;
end;


Шифрование и расшифровка:
Код: Выделить всё
function EncryptRsa(KeyPair: PRSA; var OrigMsg: PByte; LenMsg: integer;
  var EncMsg: PByte; var EncLen: integer; var err: PChar): integer;
begin
  EncLen := RSA_public_encrypt(LenMsg, OrigMsg, EncMsg, KeyPair, RSA_PKCS1_OAEP_PADDING);
  if EncLen = -1 then
  begin
    ERR_load_crypto_strings();
    Err_Error_String(ErrGetError(), err);
    Result := 0;
  end
  else
    Result := EncLen;
end;

function DecryptRsa(KeyPair: PRSA; var OrigMsg: PByte; var LenMsg: integer;
  var EncMsg: PByte; var EncLen: integer; var err: PChar): integer;
begin
  LenMsg := RSA_private_decrypt(EncLen, EncMsg, OrigMsg, KeyPair,
    RSA_PKCS1_OAEP_PADDING);
  if LenMsg = -1 then
  begin
    ERR_load_crypto_strings();
    Err_Error_String(ErrGetError(), err);
    Result := 0;
  end
  else
    Result := LenMsg;
end;


Ну и тестовый код демонстрации шифровки и расшифровки:
Код: Выделить всё

function GetHexTable(Buf: Pointer; iLen: integer): string;
var
  i: integer;
begin
  Result := '';
  for i := 0 to iLen do
    Result += IntToHex(integer(PByte(Buf + i)^), 2) + ', ';
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  PriKey:  string;
  PubKey:  string;
  RSA:     PRSA;
  OrigMsg, EncMsg: PChar;
  EncLen:  integer;
  err:     PChar;
  OrigLen: integer;
begin
  Memo.Clear;
  // Генерируем пару ключей
  PubKey := '';
  PriKey := '';
  RSA    := GenRsaKeys(512, PriKey, PubKey);

  // Отображаем это в мемо
  memo.Append(PriKey);
  memo.Append('');
  memo.Append(PubKey);
  Memo.Append('');

  // Выделяем память
  Getmem(err, MAX_PATH);
  Getmem(OrigMsg, MAX_PATH);

  // Выводим оригинальное сообщение и закидываем это в буффер
  Memo.Append('--- ORIG MSG ---');
  Memo.Append('Hello World');
  strcopy(PChar(OrigMsg), PChar('Hello World'));
  OrigLen := strlen(OrigMsg);

  // Получаем будущий размер закодированных данных
  EncLen := RSA_size(RSA);
  GetMem(EncMsg, EncLen);

  // Кодируем данные
  EncLen := EncryptRsa(RSA, PBYTE(OrigMsg), OrigLen, PByte(EncMsg), EncLen, err);
  if EncLen = 0 then
    ShowMessage(err);

  // Выводим в HEX виде закодированные данные
  Memo.Append('');
  Memo.Append('--- ENCODED MESSAGE ---');
  Memo.Append(GetHexTable(EncMsg, EncLen));
  Memo.Append('');

  // Перезаписывем буфер
  Memo.Append('--- DESTROY BUFFER ---');
  Memo.Append('Destroy data in buffer (=');
  strcopy(PChar(OrigMsg), PChar('Destroy data in buffer (='));
  Memo.Append('');

  // Расшифровываем данные
  OrigLen := DecryptRsa(RSA, PBYTE(OrigMsg), OrigLen, PByte(EncMsg), EncLen, err);
  if OrigLen = 0 then
    ShowMessage(err);

  // Выводим расшифрованные данные
  Memo.Append('--- DECODED MSG ---');
  PByte(OrigMsg+OrigLen)^ := 0;
  Memo.Append(OrigMsg);

  // Освобождаем буферы за собой
  Freemem(err);
  Freemem(OrigMsg);
  Freemem(EncMsg);
end;


Вроде всё работает, единственная проблема заключается в том, что я не могу (или не понимаю) как сохранить ключи и загрузить их обратно, буду очень признателен за помощь...

Добавлено спустя 5 часов 45 минут 58 секунд:
В общем вернувшись с работы, залез в доки и понял, что ключи вида
Код: Выделить всё
-----BEGIN RSA PRIVATE KEY-----
-----BEGIN RSA PUBLIC KEY-----

Называются PEM ключами или ключами в PEM формате, соответственно их можно загрузить из памяти или из файла, что бы загрузить из файлы следует использовать функцию fopen и передавать в файловый дескриптор в
Код: Выделить всё
RSA *PEM_read_RSA_PUBKEY(FILE *fp, RSA **x, pem_password_cb *cb, void *u);
...
и должно быть аналогично для private key


Но совместим ли FILE fpc и C?! хз, должен быть по идеи совместим, ведь дескрипторы выдаёт ОС, а вот как они хранятся в проге вот это без IDA не разберешь... По этому проще открыть файл или считать из БД в буфер и от него создать BIO_new_mem_buf и от него уже работать как с обычным BIO, исходника пока нет, я спать, если что выложу в своём блоге...
RuCode
незнакомец
 
Сообщения: 2
Зарегистрирован: 15.07.2014 19:24:30

Re: OpenSSL -> LibSSL

Сообщение SSerge » 16.07.2014 04:41:47

RuCode писал(а):Но совместим ли FILE fpc и C?! хз, должен быть по идеи совместим, ведь дескрипторы выдаёт ОС


Неверно. Структура FILE в C не имеет прямого отношения к системным вызовам. Для прямых системных обращений используются ф-и open - read - write - close и подобные им, работающие с дескрипторами (handle) (io.h, ЕМНИП), то что содержится в stdio.h и работает со ссылкой на структуру FILE - функции буферизованного ввода/вывода. Буферизация целиком на совести исполняющей системы - т.е. библиотек конкретного компилятора. В fpc FILE и структура совершенно другая, и работает все по другому. Если, конечно, вы не заимствуете FILE из какой-нибудь библиотеки, где ее специально адаптировали под интерфейс с Си, но тога по всем правилам и название было бы другим.
SSerge
энтузиаст
 
Сообщения: 971
Зарегистрирован: 12.01.2012 05:34:14
Откуда: Барнаул

Re: OpenSSL -> LibSSL

Сообщение RuCode » 16.07.2014 11:51:41

SSerge Да глянул в дизассемблере они совершенно разные... А может кто знает готовые классы\компоненты\пакеты для работы с LibSSL? Честно скажу что ни чего подходящего не нашел, но мало ли кто уже писал своё решение, а то напрягает так обезьянничать...
RuCode
незнакомец
 
Сообщения: 2
Зарегистрирован: 15.07.2014 19:24:30


Вернуться в Сторонние средства

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 14

Рейтинг@Mail.ru