PGPSDK - ЛЕГКИЙ ПУТЬ К ШИФРОВАНИЮ

Автор: Евгений Дадыков
Источник: www.delphikingdom.com
Опубликовано: 22.10.2002
Версия текста: 1.1

Введение
Небольшая справка по PGP
Поддерживаемые алгоритмы
Реализуемые функции
Обзор существующих библиотек.
Собственно сам PGPsdk
Установка
Переходим к делу.
Итак, поехали!
О настройках
Как получить список всех имеющихся ключей
Вычисление размера зашифрованного текста
Интерфейс пользователя
Вывод
Список литературы и интернет ресурсов

Демонстрационная программа - исходные тексты (Delphi)
Демонстрационная программа - исполняемый файл (для запуска необходимы dll от PGPsdk)
PGP_SDK.dll, PGPsdkNL.dll, PGPsdkUI.dll

Введение

Иногда бывает нужно прикрутить к своей программе какое-нибудь шифрование. Для этих целей разработаны кучи алгоритмов шифрования, дешифрования, электронной подписи и т.п., основанных на различных математических аппаратах. Но этого мало – алгоритмы необходимо реализовать. Мы не будем делать этого сами, а возьмем готовую библиотеку PGPsdk. Я только повторю слова классиков [2] о том, что для реальных приложений использовать самодельные шифры не рекомендуется, если вы не являетесь экспертом и не уверены на 100 процентов в том, что делаете. Отговаривать же вас от разработки собственных шифров или реализации какого-либо стандарта не входит в задачи этой статьи, здесь пойдет речь о том, как быстро и правильно реализовать в своих приложениях защиту от посторонних глаз и, самое главное - не обмануться. В моем приложении уже использовалось работающее через командные (*.bat) файлы шифрование от PGP., что явилось весомым аргументом для выбора средства шифрования. Все работало, но меня это не устраивало – DOS-овская версия PGP (5.0) затрудняла установку программы, поддержку, и не имела некоторых полезных вещей, нужных для расширения системы в будущем. Но PGP привлекала бесплатностью для некоммерческих программ, генерацией произвольного количества ключей и своей широкой распространенностью.

Небольшая справка по PGP

Pretty Good Privacy (PGP) выпущено фирмой Phil's Pretty Good Software и является криптографической системой с высокой степенью секретности для операционных систем MS-DOS, Unix, VAX/VMS и других. PGP позволяет пользователям обмениваться файлами или сообщениями с использованием функций секретности, установлением подлинности, и высокой степенью удобства. Секретность означает, что прочесть сообщение сможет только тот, кому оно адресовано. Установление подлинности позволяет установить, что сообщение, полученное от какого-либо человека, было послано именно им. Нет необходимости использовать секретные каналы связи, что делает PGP простым в использовании программным обеспечением. Это связано с тем, что PGP базируется на мощной новой технологии, которая называется шифрованием с "открытым ключом".

Поддерживаемые алгоритмы

Реализуемые функции

Обзор существующих библиотек.

Первое, что я сделал – сходил на torry.ru и был удивлен обилием библиотек и функций для разного рода шифрования. Функциональность их я проверять не стал, а остановился на компонентах PGP.

DOS-овская PGPComp, работающая по принципу запуска внешнего exe-файла, сразу отпала по простой причине – нужно будет устанавливать DOS-версию PGP (кроме того, библиотека работает только под Delphi 1 и 2). Вспомнил, что в мою любимую почтовую программу The Bat встроена поддержка PGP, зашел на ее сайт и скачал любезно предоставляемую библиотеку dklib.dll, . Но почему-то у меня ни один из примеров не пошел, а за отсутствием исходников, я не мог понять почему. Пробовал обратиться к автору – в ответ тишина, уже больше года он не отвечает. А неплохая библиотека, по крайней мере, судя по документации. Присутствует необходимый минимум функций для шифрования - расшифрования, проверки ключа, а сама библиотека весит не очень много – 184’832 Байт.

Меня не устроили эти библиотеки, и я отправился на http://www.pgpi.org/ в поисках истины. Там я нашел упоминание о библиотеке для разработчиков – PGPsdk.

Собственно сам PGPsdk

28 октября 1997 г. PGP, Inc. объявила о поставке PGPsdk сторонним производителям программного обеспечения. PGPsdk - это средство разработки для программистов на С, позволяющее разработчикам программного обеспечения встраивать в свои приложения стойкие криптографические функции. Можно сказать, что в PGPsdk реализованы все функции пакета PGP, мало того - Win-версия PGP, начиная с 5.0, хранит криптографические функции в динамических библиотеках.

PGPsdk – это динамическая библиотека, состоящая из трех файлов [табл. 1]. Она поддерживает базовые криптографические алгоритмы (перечислены выше), гибкое управление ключами, сетевой интерфейс и др. (можно обойтись одной библиотекой - PGP_sdk.dll, если вы не будете использовать фирменный интерфейс пользователя от NAI и сетевые функции).

Установка

Скачайте архив с PGPsdk [7], (на момент написания статьи доступна версия 1.7.2, размер архива около 3-х мегабайт), разверните его и возьмите следующие файлы из каталога \Libraries\DLL\Release (табл. 1)

PGP_SDK.dllдля шифрования, управления ключами и т.д.
PGPsdkUI.dll(UI= user interface) интерфейсные штучки, если нужно только шифровать/расшифровывать, этот файл необязателен. Но он очень полезен для ввода пароля, выбора получателей сообщений, генерации ключей и многого другого.
PGPsdkNL.dll(NL= network library) сетевая библиотека для работы с сервером ключей или для transport layer security. Сейчас мы ее рассматривать не будем, но в ближайшем будущем я попытаюсь ее описать.

Распространять приложение придется с этими файлами, их следует поместить или в системный каталог, или в каталог приложения - главное, чтобы библиотека была доступна .

Переходим к делу.

Для работы система предоставляет ряд низкоуровневых PGP API-функций. Заголовочные файлы этих функций на С поставляются вместе с пакетом и лежат в каталоге Headers. Если вы, как и я, пишете на Delphi, можете сами конвертировать их, а можете взять готовые тут [10]. Это проект по переводу заголовочных файлов С на любимый мною язык программирования. Занимается всем этим делом Стивен Хейлер (Steven R. Heller <srheller@oz.net>).

При переводе на Delphi была сохранена структура заголовочных файлов С. Все названия модулей аналогичны заголовкам С, за исключением того, что pgpEncode переименовано в pgpEncodePas из-за особенностей объявления в Delphi (нельзя, чтобы имя процедуры совпадало с названием модуля).

// Объявление используемых библиотек.
uses
  // PGPsdk
  pgpEncodePas, pgpOptionList, pgpBase, pgpPubTypes,
  pgpUtilities, pgpKeys, pgpErrors,
  // always last
  pgpSdk;

Единственная трудность, которая возникает на пути включения криптографии в приложение – это использование слишком уж низкоуровневых API-функций PGP. Чтобы сделать какую-нибудь операцию – будь то подсчет открытых ключей в связке или просто шифрование файлов, необходимо произвести множество действий. Применение PGP API в Delphi показано в листинге 2.

Листинг 2. Пример использования PGPsdk через PGP API
Var
  context        : pPGPContext;
  keyFileRef     : pPGPKeySet;
  defaultKeyRing : pPGPKeySet;
  foundUserKeys  : pPGPKeySet;
  filter         : pPGPFilter;
  countKeys      : PGPUInt32;
  keyFileName    : PChar;
  userID         : PChar;
  inFileRef,
  outFileRef     : pPGPFileSpec;
  inFileName,
  outFileName    : PChar;
Begin
  // Init от C++
  context:=NIL;
  keyFileName:='pubring.pgp';
  userID:='';
  inFileName:='myInFile.txt';
  outFileName:='myOutFile.txt.asc';

  // Begin
  PGPCheckResult('sdkInit', PGPsdkInit);

  PGPCheckResult('PGPNewContext',
                  PGPNewContext(
                                kPGPsdkAPIVersion,
                                context
                               ));

  PGPCheckResult('PGPNewFileSpecFromFullPath',
                  PGPNewFileSpecFromFullPath(
                                             context,
                                             keyFileName,
                                             keyFileRef
                                            ));


  PGPCheckResult('PGPOpenKeyRing',
                  PGPOpenKeyRing(
                                 context,
                                 kPGPKeyRingOpenFlags_None,
                                 keyFileRef,
                                 defaultKeyRing
                                ));

  PGPCheckResult('PGPNewUserIDStringFilter',
                  PGPNewUserIDStringFilter(context, userID, kPGPMatchSubString, filter));

  PGPCheckResult('PGPFilterKeySet',
                  PGPFilterKeySet(defaultKeyRing, filter, foundUserKeys));

  // Открываем файловые манипуляторы
  PGPCheckResult('PGPNewFileSpecFromFullPath',
                  PGPNewFileSpecFromFullPath(context, inFileName, inFileRef));

  PGPCheckResult('PGPNewFileSpecFromFullPath',
                  PGPNewFileSpecFromFullPath(context, outFileName, outFileRef));

  //
  // А вот здесь уже идет кодирование.
  //
  PGPCheckResult('PGPEncode',
                  PGPEncode(
                  context,
                  [
                   PGPOEncryptToKeySet(context, foundUserKeys),
                   PGPOInputFile(context, inFileRef),
                   PGPOOutputFile(context, outFileRef),
                   PGPOArmorOutput(context, 1),
                   PGPOCommentString(context, PChar('Comments')),
                   PGPOVersionString(context, PChar('Version 5.0 assembly by Evgeny Dadgoff')),
                   PGPOLastOption(context)
                  ]
                ));

  
  //
  // Освобождаем занимаемые ресурсы и контекст PGP
  //
  if (inFileRef<>NIL) then PGPFreeFileSpec(inFileRef);
  if (outFileRef<>NIL) then PGPFreeFileSpec(outFileRef);
  if (filter<>NIL) then PGPFreeFilter(filter);
  if (foundUserKeys<>NIL) then PGPFreeKeySet(foundUserKeys);
  if (defaultKeyRing<>NIL) then PGPFreeKeySet(defaultKeyRing);
  if (keyFileRef<>NIL) then PGPFreeKeySet(keyFileRef);
  if (context<>NIL) then PGPFreeContext(context);
  PGPsdkCleanup;
End;

При написании этого кода за основу взят пример из PGPsdk Users Guide [9].

Для обработки ошибок использована функция PGPCheckResult, позаимствованная у Стивена Хейлера. Она имеет два параметра – строку, через которую передается некоторое описание, и код выполнения функции PGP API. Если передан код ошибки, эта функция генерирует исключение, избавляя от долгих ручных проверок.

Листинг 3. Функция PGPCheckResult.
procedure PGPCheckResult(const ErrorContext: shortstring; const TheError: PGPError);
var
  s : Array [0..1024] of Char;
begin
  if(TheError <> kPGPError_NoError)then
  begin
    PGPGetErrorString(TheError, 1024, s);
    if(PGPGetErrorString(TheError, 1024, s) = kPGPError_NoError)then
      raise exception.create(ErrorContext + ' [' + IntToStr(theError)+'] : '+StrPas(s))
    else
      raise exception.create(ErrorContext + ': Error retrieving error description');
  end;
end;

Там же, у Стивена я нашел написанную на Delphi библиотеку для VB, проект под названием SimplePGP (SPGP). Дело в том, что в VB использование библиотек типа PGPsdk осложнено [9, раздел FAQ]. Сам Стивен предложил мне добавить к проекту еще одну dll, забыть про PGP API, и использовать облегченную модель вызова криптографических функций.

Я принял решение избавиться от DLL и присоединить весь код к ехе-файлу. Сказано - сделано.

Итак, поехали!

Создадим каталог PGPsdk и скопируем туда файлы DELPHI PGP API - pgp*.pas и spgp*.pas. Удалим в файлах spgp*.pas "stdcall;export;" (получившиеся в итоге файлы можно взять в исходных текстах демонстрационной программы). Теперь нужно добавить описание используемых библиотек:

uses
  // PGPsdk
  pgpEncodePas, pgpOptionList, pgpBase, pgpPubTypes,
  pgpUtilities, pgpKeys, pgpErrors,
  // SPGP
  spgpGlobals, spgpEncrypt, spgpKeyUtil, spgpUtil, spgpKeyMan,
  spgpPreferences, spgpKeyProp, spgpKeyIO, spgpKeyGen, spgpMisc,
  spgpUIDialogs,
  // always last
  pgpSdk;

Первое, что мы попробуем сделать - это зашифровать и подписать произвольный файл, и получить результат в текстовом виде (ASCII). Следует отметить, что PGPsdk может работать не только с файлами, но и с данными, находящимися в памяти.

PGPCheckResult
    (
     'Ошибка при шифровании файла',
     spgpencodefile(
                    PChar(edtFileIn.Text),
                    PChar(edtFileOut.Text),
                    1,                              // Encrypt.Value
                    1,                              // Sign.Value
                    kPGPHashAlgorithm_MD5,
                    0,
                    kPGPCipherAlgorithm_CAST5,
                    1,
                    0,
                    0,
                    'Steven R. Heller',             // Кто может расшифровать
                    'Evgeny Dadgoff',               // Чем подписывать
                    'MyPassPhrase',                 // Это пароль
                    '',
                    PChar(edtComment.Text)
                   )
    );

Если сравнить этот пример с примерами из [9], становится ясно, насколько проще использовать SPGP.

О настройках

Для работы библиотеке необходимо знать, где лежат файлы с ключами (pubring.prk и secring.prk). Настройки PGP API хранятся в файле PGPsdk.dat (он находится в каталоге Windows). Для работы с этим файлом предназначены следующие функции:

spgpGetPreferences(Prefs: pPreferenceRec; Flags: Longint):LongInt;
spgpSetPreferences(Prefs: pPreferenceRec; Flags: Longint):LongInt;

для чтения и сохранения настроек соответственно.

Замечу, что это не единственный способ – PGP API позволяет напрямую указывать, где расположены ключи. Но тогда придется отказаться от использования SPGP или править исходный код SPGP.

Как получить список всех имеющихся ключей

Код, приведенный ниже, показывает, как заполнить список LVKeys именами и шестнадцатеричными ID-значениями ключей, используя SPGP.

Var
  P                  : TPreferenceRec;
  Flags              : LongInt;
  outBuf             : array [1..30000] of Char;
  i,KeyCount         : Integer;
  TempStr,StrKeys    : AnsiString;
Begin
  LVKeys.Items.Clear;
  FillChar(P,1024,0);
  FillChar(outbuf,30000,0);
  Flags:= PGPPrefsFlag_PublicKeyring or
          PGPPrefsFlag_PrivateKeyring or
          PGPPrefsFlag_RandomSeedFile;
  if(spgpGetPreferences(@P, Flags)<>0) then ShowEvent('Error!',1);
  // GetWindowsDirectory
  if(LowerCase(WinDir+'pubring.pkr')=LowerCase(StrPas(P.PublicKeyring)))or not(FileExists(StrPas(P.PublicKeyring))) then
     Begin
     StrPCopy(P.PublicKeyring,  ExtractFilePath(Application.ExeName)+'KEYS\pubring.pgp');
     StrPCopy(P.PrivateKeyring, ExtractFilePath(Application.ExeName)+'KEYS\secring.pgp');
     StrPCopy(P.RandomSeedFile, ExtractFilePath(Application.ExeName)+'KEYS\randseed.bin');
     if (CreateDir(ExtractFilePath(Application.ExeName)+'KEYS')) Then
        ShowEvent('Каталог ключей '+ExtractFilePath(Application.ExeName)+'KEYS'+' -- не существует, Будет создан заново... ',0);
     spgpSetPreferences(@P, Flags);

     //Создать файлы с ключами - такой хитрый прием.
     spgpSubKeyGenerate('mmmh', 'sssl', 'ssss', 1, 1024, 0, 0, 0, 0);
End;
  btnPubKeys.Caption:=StrPas(P.PublicKeyring);
  btnSecKeys.Caption:=StrPas(P.PrivateKeyring);
  btnRndBin.Caption:=StrPas(P.RandomSeedFile);
  PGPCheckResult('Ошибка при инициализации PGP-SDK, убедитесь что все DLL установлены правильно',
                          Init(FContext, PubKey, false, false));
  spgpKeyRingID(@outBuf, 30000);
  KeyCount:=spgpkeyringcount;
  StrKeys:=StrPas(@outBuf);
  for i:=1 to KeyCount do
    Begin
    TempStr:=Copy(StrKeys,1,Pos(#13+#10,StrKeys));
    Delete(StrKeys,1,Pos(#13+#10,StrKeys)+1);
    with(LVKeys.Items.Add)do
      Begin
      Caption:=Copy(TempStr,14,Length(TempStr)-14);
      SubItems.Add(TempStr[1]);
      SubItems.Add(Copy(TempStr,3,10));
      End;
    End;
  QuitIt(FContext, PubKey);
End;

Вычисление размера зашифрованного текста

Не всегда можно предположить, какой размер будет иметь зашифрованный текст. Функции, проводящие преобразование, требуют выделить память под него заранее, и, если памяти не хватает, возвращают сообщение об ошибке. Опытным путем мне удалось установить формулу вычисления размера блока:

outBufLen := inBufLen*5;
if(outBufLen<10000) then outBufLen:=10000;
outBufRef := StrAlloc(outBufLen);

Интерфейс пользователя

PGP_sdkUI.dll – это библиотека пользовательских интерфейсов от Network Associates, включающая такие же диалоги ввода пароля, генерации ключей и выбора получателей сообщений, как и у фирменного пакета PGP.

Вывод

Функции криптографии можно применить в самых разных приложениях, а PGP позволяет делать это быстро, надежно, открыто и самое главное – переносимо. Но я могу посоветовать еще одно применение - это защита программ от несанкционированного копирования - зашить открытый ключ в exe-файл, и рассылать секретный нужным людям.

Список литературы и интернет ресурсов

  1. Владимир Жельников "Криптография от папируса до компьютера" М:ABF, 1996
  2. Tatu Ylonen "Introduction to Cryptography"
  3. Брюс Шнайер "Прикладная криптография"
  4. http://www.ssl.stu.neva.ru/psw/crypto.html
  5. http://pgp2all.org.ru/
  6. http://www.pgpi.org/cgi/download.cgi?filename=pgp50ibi.zip
  7. ftp://ftp.no.pgpi.org/pub/pgp/sdk/
  8. PGP Software Developer’s Kit "PGPsdk, Reference Guide Version 1.7"
  9. PGP Software Developer’s Kit "PGPsdk, Users Guide Version 1.7"
  10. http://www.oz.net/~srheller/dpgp/sdk/
  11. http://www.oz.net/~srheller/spgp/

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