Использование PGP SDK
Часть 1. Шифрование с открытым
ключом.
Введение
“Q: Вот говорят иногда "симметричные шифры", "криптография с открытым ключом". Поясните, что это за разделение?
A1: Симметричные шифры (криптосистемы) - это такие шифры, в которых для зашифрования и расшифрования информации используется один и тот же ключ. В несимметричных системах (системах с открытым ключом) для зашифрования используется открытый (публичный) ключ, известный всем и каждому, а для расшифрования - секретный (личный, закрытый) ключ, известный только получателю.”
RU.CRYPT FAQ /Часто задаваемые вопросы конференции RU.CRYPT/
“PGP основывается на широко распространенной и получившей достаточное признание системе “шифрования с открытым ключом”, в соответствии с которой Вы, так же, как и другие пользователи PGP генерируете пару ключей, состоящую из закрытого и открытого ключей. Закрытый ключ, как и следует из его названия, доступен только Вам. Но для того, чтобы Вы смогли общаться с другими пользователями PGP Вам нужны копии их открытых ключей, а им – копии Вашего. Закрытый ключ Вы используете для подписи сообщений и файлов, отправляемых другим, а также для расшифровки сообщений и файлов, отправляемых Вам. Открытые ключи других Вы используете для шифрования почты, направляемой им, и для верификации их цифровой подписи.”
PGP для Персональной Приватности. Руководство пользователя.
"Q: What operating systems does PGPsdk support?"
A:
- Windows 95 OSR2, 98, or NT with SP 4 or later
- Mac OS System 7.6.1. or later
- Linux x86 witch 2.0.x kernel or later
- Sun Solaris 2.5.1, 2.6, or 2,7"
PGP Software Developer's Kit. User's Guide.
Алгоритмы, реализованные в PGP SDK:
- симметричные – IDEA, 3DES, CAST5
- несимметричные – RSA, ElGamal ( a.k.a.Diffie-Hellman ), DSA
- хеширование – MD5, SHA, RIPEMD160
Все эти алгоритмы могут быть использованы как в составе высокоуровневых операций, например, шифровании с открытым ключом с наложением цифровой подписи, так и каждый в отдельности, вызовом соответствующих функций PGP SDK.
Установка PGP SDK
В PGP SDK входят заголовочные файлы, библиотеки, документация с описанием функций и тестовые примеры. Установка заключается в копировании архива PGP SDK, распаковки его в отдельный каталог и прописывании путей к каталогам Headers (Include files) и Libraries\DLL\Release (Library files) в настройках Visual Studio (Tools - Options - Projects - VC++ Directories).
ПРИМЕЧАНИЕ К сожалению, разработчики SDK дали одинаковые имена debug и release – версиям dll PGP SDK, поэтому приходится копировать debug версии dll в каталог с debug – версией каждой разрабатываемой программы, либо настраивать все проекты так, чтобы исполняемые файлы создавались в одном каталоге с debug версией PGP_SDK.dll. |
Вот несколько адресов, по которым можно скачать PGP SDK:
http://www.pgpi.org/products/sdk/c++/pgpsdk/
ftp://ftp.hacktic.nl/pub/crypto/pgp/pgpsdk/
ftp://ftp.no.pgpi.org/pub/pgp/sdk/
При компиляции демонстрационной программы использовался PGP SDK версии 1.7.2.
Если вам встретятся затруднения в процессе использования функций PGP SDK вам прямая дорога в исходники одной из версий PGP.
Демонстрационная программа к данной части может только шифровать данные, чтобы их расшифровать, воспользуйтесь программой PGP. Она также пригодится Вам для генерации ключей при экспериментах с PGP SDK, по крайней мере, пока Вы не научите делать это свою программу.
Функции PGP SDK, используемые при шифровании с открытым ключом
Рассмотрим вкратце функции участвующие в процессе шифрования данных с помощью PGP SDK. Общие сведения, необходимые для понимания работы некоторых функций будут даваться по ходу дела. Самой первой функцией, вызываемой при работе с PGP SDK должна быть
PGPsdkInit
PGPError PGPsdkInit( void );
|
Соответственно завершается работа с PGP SDK вызовом
PGPsdkCleanup
PGPError PGPsdkCleanup( void );
|
Возвращаемое значение функций PGP SDK имеет тип PGPError, и может быть проверено при помощи макросов
IsPGPError и IsntPGPError
#define IsPGPError( err ) ( (err) != kPGPError_NoErr ) #define IsntPGPError( err ) ( (err) == kPGPError_NoErr ) |
Для получения текстового описания ошибки по ее коду предназначена
PGPGetErrorString
PGPError PGPGetErrorString(
PGPError theErrorCode,
PGPsize availLength,
char *theErrorText );
|
theErrorCode | Код ошибки для анализа |
---|---|
availLength | Размер передаваемого в функцию буфера |
theErrorText | Указатель на буфер, в котором будет размещен текст ошибки |
Собственно шифрование данных с открытым ключом осуществляется функцией
PGPEncode
PGPError PGPEncode( PGPContextRef pgpContext, PGPOptionListRef firstOption, ..., PGPOLastOption() ); |
pgpContext | Используемый PGP-контекст. Фактически представляет собой совокупность глобальных переменных, совместно используемых функциями PGP SDK. |
---|---|
firstOption | Первая опция шифрования |
... | Список других необходимых опций |
PGPOLastOption() | Последняя опция в списке, показывает, что список опций закончен |
Используемый в качестве одного из параметров PGP-контекст создается после успешного вызова PGPsdkInit при помощи функции
PGPNewContext
PGPError PGPNewContext( PGPUInt32 clientAPIVersion, PGPContextRef *pgpContext ); |
clientAPIVersion | Версия клиентского API PGP SDK. Чтобы это не значило : -) при вызове функции этот параметр должен быть равен kPGPsdkAPIVersion. |
---|---|
pgpContext | Указатель на создаваемый PGP контекст. |
По окончании работы с функциями SDK, но до вызова PGPsdkCleanup, нужно вызвать
PGPFreeContext
PGPError PGPFreeContext( PGPContextRef pgpContext ); |
pgpContext | Освобождаемый PGP контекст. |
---|
ПРЕДУПРЕЖДЕНИЕ К моменту вызова этой функции должны быть освобождены все ресурсы, созданные при работе с функциями SDK. Если вы забудете это сделать, debug версии библиотек PGP SDK напомнят вам об этом одним из своих ASSERT-ов. |
Для передачи в PGPEncode и в PGPImportKeySet (см. ниже) нам понадобятся опции, создаваемые следующими функциями –
PGPOArmorOutput
PGPOptionListRef PGPOArmorOutput( PGPContextRef pgpContext, PGPBoolean armorOutput ); |
pgpContext | Используемый PGP-контекст. |
---|---|
armorOutput | Устанавливается в TRUE, если нужно получить выходные данные в виде ASCII. |
Без явного указания этой опции выходные данные создаются в бинарном формате.
PGPODataIsASCII
PGPOptionListRef PGPODataIsASCII( PGPContextRef pgpContext, PGPBoolean dataIsASCII ); |
pgpContext | Используемый PGP-контекст. |
---|---|
dataIsASCII | Установите в TRUE, если входные данные - в виде ASCII. |
Если при зашифровании текста была установлена данная опция, при его расшифровке символы перевода строки <CR><LF> будут оттранслированы в символы соответствующего назначения для платформы, на которой происходит расшифровка. (см. перечень платформ во Введении)
PGPOInputFile
PGPOptionListRef PGPOInputFile( PGPContextRef pgpContext, PGPFileSpecRef fileSpec ); |
pgpContext | Используемый PGP-контекст. |
---|---|
fileSpec | Заранее подготовленная файловая спецификация |
Опция указывает, что входные данные функция должна будет брать из файла.
PGPOOutputFile
PGPOptionListRef PGPOOutputFile( PGPContextRef pgpContext, PGPFileSpecRef fileSpec ); |
pgpContext | Используемый PGP-контекст. |
---|---|
fileSpec | Заранее подготовленная файловая спецификация |
Опция указывает, что выходные данные функция должна будет разместить в файле.
Файловую спецификацию, необходимую для передачи в PGPOInputFile и PGPOOutputFile можно создать вызовом функции
PGPNewFileSpecFromFullPath
PGPError PGPNewFileSpecFromFullPath( PGPContextRef pgpContext, char const *pathname, PGPFileSpecRef *fileRef ); |
pgpContext | Используемый PGP-контекст. |
---|---|
pathname | Имя файла, для которого создается спецификация |
fileRef | Указатель на создаваемую спецификацию |
По окончании использования (до вызова PGPFreeContext) файловая спецификация должна быть освобождена вызовом
PGPFreeFileSpec
PGPError PGPFreeFileSpec( PGPFileSpecRef fileSpecRef ); |
fileSpecRef | Освобождаемая файловая спецификация. |
---|
PGPOInputBuffer
PGPOptionListRef PGPOInputBuffer( PGPContextRef pgpContext, void const *inBuf, PGPSize inBufLength ); |
pgpContext | Используемый PGP-контекст. |
---|---|
inBuf | Указатель на буфер с данными |
inBufLength | Размер передаваемого буфера |
Опция указывает, что входные данные функция должна будет брать из буфера.
PGPOOutputBuffer
PGPOptionListRef PGPOOutputBuffer(
PGPContextRef pgpContext,
void *outBuf,
PGPSize outBufLength,
PGPSize *outBufDataLength );
|
pgpContext | Используемый PGP-контекст. |
---|---|
outBuf | Указатель на буфер, в который будут помещены данные |
outBufLength | Размер передаваемого буфера |
outBufDataLength | Размер данных, которые помещены (должны быть помещены) в буфер. Заполняется вызываемой функцией. |
Если размер данных, которые нужно поместить в буфер окажется больше, чем размер переданного буфера, PGPEncode вернет ошибку kPGPError_OutputBufferTooSmall, а в outBufDataLength будет помещен требуемый размер буфера. Можно либо эмпирически подсчитывать (с запасом) размер выходного буфера на основании размера входных данных, либо вызывать PGPEncode минимум 2 раза - сначала чтобы получить размер выходных данных, а затем чтобы получить собственно требуемые данные:
// начальный размер буффера // заведомо маленький, после первого вызова PGPEncode в // параметре PGPOOutputBuffer будет передано действительно требуемое // значение размера буфера // ( 0 в качестве начального значения не проходит, поэтому 1 ) BuffSize = 1; // собственно шифрование do { delete[] OutData; // Выделенную здесь память надо освобождать по окончании использования // выходного буфера OutData = new BYTE[ BuffSize ]; err = PGPEncode( m_context, // входной буфер PGPOInputBuffer( m_context, ( void* ) inData, ( PGPSize ) dwDataSize ), // выходной буфер PGPOOutputBuffer( m_context, ( void* ) OutData, ( PGPSize ) BuffSize, ( PGPSize *) &BuffSize ), // шифровать на всех ключах из набора PGPOEncryptToKeySet( m_context, pubKeySet ), // и предварительно сформированный при // инициализации список опций m_optsEncode, // больше опций не будет PGPOLastOption( m_context ) ); } while ( err == kPGPError_OutputBufferTooSmall ); |
ПРИМЕЧАНИЕ Данные, помещаемые в буфер, не завершаются в конце никаким спец. символом, поэтому если вы решите поместить эти данные в строку, необходимо позаботиться о добавлении в нужном месте null-терминатора. |
PGPOEncryptToKeySet
PGPOptionListRef PGPOEncryptToKeySet( PGPContextRef pgpContext, PGPKeySetRef keySet ); |
pgpContext | Используемый PGP-контекст. |
---|---|
keySet | Набор ключей шифрования. |
Опция указывает, что данные должны быть зашифрованы на каждом из ключей, входящем в набор.
Это также означает, что зашифрованные таким образом данные могут быть расшифрованы с помощью любого из закрытых ключей, чья пара (открытый ключ) входила в набор ключей для зашифрования.
Ключи можно импортировать в набор из файла или буфера в памяти при помощи функции
PGPImportKeySet
PGPError PGPImportKeySet( PGPContextRef pgpContext, PGPKeySetRef *keySet, PGPOptionListRef firstOption, ..., PGPOLastOption() ); |
pgpContext | Используемый PGP-контекст. |
---|---|
KeySet | Указатель на набор, в который нужно импортировать ключи |
firstOption | Первая опция настройки импорта |
... | Список других необходимых опций |
PGPOLastOption() | Последняя опция в списке, показывает, что список опций закончен |
Для импорта ключей из файла нужно указывать опцию PGPOInputFile, а для импорта из буфера – PGPOInputBuffer.
По окончании использования (до вызова PGPFreeContext) набор ключей должен быть освобожден вызовом функции
PGPFreeKeySet
PGPError PGPFreeKeySet( PGPKeySetRef keySet ); |
KeySet | Освобождаемый набор ключей. |
---|
PGPOVersionString
PGPOptionListRef PGPOVersionString( PGPContextRef pgpContext, char const *versionString ); |
pgpContext | Используемый PGP-контекст. |
---|---|
versionString | Строка с информацией о версии, которая будет включена в заголовок зашифрованного сообщения |
PGPOCommentString
PGPOptionListRef PGPOCommentString( PGPContextRef pgpContext, char const * commentString ); |
pgpContext | Используемый PGP-контекст. |
---|---|
commentString | Строка-комментарий, которая будет включена в заголовок зашифрованного сообщения |
PGPOCipherAlgorithm
PGPOptionListRef PGPOCipherAlgorithm( PGPContextRef pgpContext, PGPCipherAlgorithm algID ); |
pgpContext | Используемый PGP-контекст. |
---|---|
algID | Идентификатор алгоритма, используемого для симметричного шифрования. |
В качестве идентификатора алгоритма algID могут применяться значения из следующего перечисления (pgpPubTypes.h):
enum PGPCipherAlgorithm_ { /* do NOT change these values */ kPGPCipherAlgorithm_None = 0, kPGPCipherAlgorithm_IDEA = 1, kPGPCipherAlgorithm_3DES = 2, kPGPCipherAlgorithm_CAST5 = 3, kPGPCipherAlgorithm_First = kPGPCipherAlgorithm_IDEA, kPGPCipherAlgorithm_Last = kPGPCipherAlgorithm_CAST5, PGP_ENUM_FORCE( PGPCipherAlgorithm_ ) }; PGPENUM_TYPEDEF( PGPCipherAlgorithm_, PGPCipherAlgorithm ); |
ПРИМЕЧАНИЕ Так как алгоритмы шифрования с открытым ключом значительно медленнее алгоритмов для обычного, симметричного шифрования, при использовании высокоуровневой функции PGPEncode открытым ключом шифруется только случайный сеансовый ключ, все остальные данные шифруются этим сеансовым ключом одним из симметричных алгоритмов – IDEA, 3DES или CAST5. Сказанное можно пояснить следующей картинкой из "Руководства пользователя PGP": |
ПРИМЕЧАНИЕ Алгоритм для несимметричного шифрования, определяется используемым открытым ключом. |
При вызове функций необходимые опции можно передавать, просто перечисляя через запятую, а можно предварительно создать список необходимых опций и передавать его как единое целое. Список опций создается функцией
PGPBuildOptionList
PGPError PGPBuildOptionList( PGPContextRef pgpContext, PGPOptionListRef *outList, PGPOptionListRef firstOption, ..., PGPOLastOption() ); |
pgpContext | Используемый PGP-контекст. |
---|---|
OutList | Указатель на создаваемый список опций |
firstOption | Первая опция в списке |
... | Список других необходимых опций |
PGPOLastOption() | Последняя опция в списке, показывает, что список опций закончен |
По окончании использования (до вызова PGPFreeContext) список опций нужно очистить вызовом
PGPFreeOptionList
PGPError PGPFreeOptionList( PGPOptionListRef optionList ); |
optionList | Существующий список опций, который нужно очистить. |
---|
ПРИМЕЧАНИЕ Выше перечислены только функции и опции, использованные в CSimplePGP. Полный список возможностей, предоставляемых PGP SDK для шифрования данных гораздо шире, ознакомиться с ним можно в Reference Guide PGP SDK. |
Класс CSimplePGP
Назначение
Использование PGP SDK напрямую не представляет особой сложности, однако весьма неудобно, т.к. для выполнения какой-либо законченной операции, например, шифрования файла, нужно последовательно выполнить с десяток вызовов функций SDK – инициализацию, подготовку ключей, файлов, выбор необходимых опций, определяющих форматы входящих и исходящих данных, используемые алгоритмы и т.д., а потом еще и провести очистку использованных ресурсов. Напрашивается создание класса-обертки, который был бы прост в использовании и в то же время закрывал на 90% наиболее часто используемые операции.
На данном этапе в классе реализованы только функции шифрования данных:
simplepgp.h#ifndef _SIMPLEPGP_H_5D07C0B9_FCAC_40A8_BB01_7536FDE1A93F #define _SIMPLEPGP_H_5D07C0B9_FCAC_40A8_BB01_7536FDE1A93F #define PGP_WIN32 1 #include <pgpErrors.h> #include <pgpUtilities.h> #include <pgpKeys.h> #include <pgpEncode.h> #include <string> #include <vector> using std::string; using std::vector; class CSimplePGP { public: CSimplePGP( void ); ~CSimplePGP( void ); protected: // PGP-контекст PGPContextRef m_context; // Опции шифрования общие для всех вариантов PGPOptionListRef m_optsEncode; // Флажок успешной инициализации BOOL m_bIsInit; // Загрузка ключей из файла BOOL ImportKeyFromFile( LPCTSTR keyFileName, PGPKeySetRef& keySet ); // Загрузка ключей из ресурсов BOOL ImportKeyFromResorce( LPCTSTR ResourceName, LPCTSTR ResourceType, PGPKeySetRef& keySet ); // описание места возникновения ошибки string m_sWhere; // расшифровка ошибки TCHAR m_sWhat[ 256 ]; // версия string m_sVersion; // комментарий string m_sComment; // формат вывода BOOL m_bASCIIoutput; // формат входных данных BOOL m_bASCIIinput; // пересобрать список опций шифрования в соответствии с новыми настройками BOOL ReBuildEncodeOptionList(); // проверка переданного указателя на выходной буфер BOOL IsOutBuffNotNull( LPBYTE& OutData ); // алгоритм для симметричного шифрования PGPCipherAlgorithm m_algoID; public: // инициализация BOOL Init(); // установка информации о версии BOOL SetVersion ( LPCTSTR sVersion ); // добавление строки - комментария BOOL SetComment ( LPCTSTR sComment ); // задать формат вывода BOOL SetASCIIoutput ( BOOL bASCIIout ); // формат входных данных BOOL SetASCIIinput ( BOOL bASCIIinp ); // алгоритм для симметричного шифрования BOOL SetCipherAlgorithm( PGPCipherAlgorithm algoID ); // получение информации об ошибке LPCTSTR GetErrPlace() { return m_sWhere.c_str(); } LPCTSTR GetErrDesc() { return m_sWhat; } // шифрование // из файла в файл, открытый ключ тоже в файле BOOL EncodeFile2File( LPCTSTR inFileName, // имя входного файла LPCTSTR outFileName, // имя выходного файла LPCTSTR keyFileName ); // имя файла с открытыми ключами // из файла в файл, открытый ключ в ресурсах BOOL EncodeFile2File( LPCTSTR inFileName, // имя входного файла LPCTSTR outFileName, // имя выходного файла LPCTSTR resourceName, // имя ресурса c открытыми ключами LPCTSTR resourceType ); // тип ресурса // из памяти в файл, открытый ключ тоже в файле BOOL EncodeBuff2File( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буффера LPCTSTR outFileName, // имя выходного файла LPCTSTR keyFileName ); // имя файла с открытыми ключами // из памяти в файл, открытый ключ в ресурсах BOOL EncodeBuff2File( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буфера LPCTSTR outFileName, // имя выходного файла LPCTSTR resourceName, // имя ресурса c открытыми ключами LPCTSTR resourceType ); // тип ресурса // В функцииях пишущих в буфер выделяется память // под буфер OutData, не забудьте освободить ее после // использования буфера, размер выделенной // памяти после выполнения функций // находится в BuffSize // из файла в буфер, открытый ключ в файле. BOOL EncodeFile2Buff( LPCTSTR inFileName, // имя входного файла LPBYTE& OutData, // указатель на буфер для данных DWORD& BuffSize, // размер буфера LPCTSTR keyFileName ); // имя файла с открытыми ключами // из файла в буфер, открытый ключ в ресурсах BOOL EncodeFile2Buff( LPCTSTR inFileName, // имя входного файла LPBYTE& OutData, // указатель на буфер для данных DWORD& BuffSize, // размер буфера LPCTSTR resourceName, // имя ресурса c открытыми ключами LPCTSTR resourceType ); // тип ресурса // из памяти в память, открытый ключ в файле BOOL EncodeBuff2Buff( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буффера LPBYTE& OutData, // указатель на буфер для данных DWORD& BuffSize, // размер буфера LPCTSTR keyFileName ); // имя файла с открытыми ключами // из памяти в память, открытый ключ в ресурсах BOOL EncodeBuff2Buff( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буфера LPBYTE& OutData, // указатель на буфер для данных DWORD& BuffSize, // размер буфера LPCTSTR resourceName, // имя ресурса c открытыми ключами LPCTSTR resourceType ); // тип ресурса }; #endif |
Как видно из описания класса, он предоставляет возможность шифрования данных из файла или из памяти и использование ключей также в виде файла или из ресурсов программы. Файл с открытым ключом (ключами), который будет использоваться для шифрования, можно получить экспортом из менеджера ключей программы PGP.
Использование
Использование CSimplePGP в программе предельно просто:
#include "simplepgp.h" CSimplePGP pgp; // инициализация PGP if ( !pgp.Init() ) ::MessageBox( this->m_hWnd, pgp.GetErrDesc(), pgp.GetErrPlace(), MB_OK | MB_ICONSTOP ); // добавляем комментарий if ( !pgp.SetComment( _T("Ключ и данные - из файлов") ) ) ::MessageBox( this->m_hWnd, pgp.GetErrDesc(), pgp.GetErrPlace(), MB_OK | MB_ICONSTOP ); // для симметричного шифрования использовать 3DES if ( !pgp.SetCipherAlgorithm( kPGPCipherAlgorithm_3DES ) ::MessageBox( this->m_hWnd, pgp.GetErrDesc(), pgp.GetErrPlace(), MB_OK | MB_ICONSTOP ); // шифрование файла sInFileName на ключе (-ах) из файла sPubKeyFileName if ( !pgp.EncodeFile2File( sInFileName, sOutFileName, sPubKeyFileName ) ) ::MessageBox( this->m_hWnd, pgp.GetErrDesc(), pgp.GetErrPlace(), MB_OK | MB_ICONSTOP ); // меняем комментарий if ( !pgp.SetComment( _T("Ключ из ресурсов, данные - из поля ввода") ) ) ::MessageBox( this->m_hWnd, pgp.GetErrDesc(), pgp.GetErrPlace(), MB_OK | MB_ICONSTOP ); // шифрование данных из строки CString sInData с записью результата в буфер // открытый ключ берется из ресурса "PGPTESTPUBKEY" типа "PGPKEYS" LPBYTE OUTBUFF = NULL; DWORD BUFFSIZE = 0; if ( !pgp.EncodeBuff2Buff( ( LPCBYTE ) ( LPCTSTR ) sInData, ( DWORD ) sInData.GetLength(), OUTBUFF, BUFFSIZE, _T( "PGPTESTPUBKEY" ), _T( "PGPKEYS" ) ) ) ::MessageBox( this->m_hWnd, pgp.GetErrDesc(), pgp.GetErrPlace(), MB_OK | MB_ICONSTOP ); else { // Используем данные из буфера OUTBUFF ... ... } delete[] OUTBUFF; |
Чтобы добавить ключевой файл в ресурсы программы нужно сначала создать New Custom Resource указав тип ресурса в виде произвольной строки в кавычках, а затем импортировать ключевой файл в ресурсы программы выбрав при добавлении указанный выше тип ресурса и задав имя ресурса также в виде произвольной строки в кавычках.
Подробности реализации
Инициализация
Нужно провести собственно инициализацию PGP SDK, создать pgp-контекст и сформировать лист опций шифрования, общий для всех режимов.
CSimplePGP::Init()// инициализация BOOL CSimplePGP::Init() { // два раза выполнять не надо if ( m_bIsInit ) return m_bIsInit; // код ошибки PGP PGPError err = kPGPError_NoErr; // инициализация PGP SDK err = PGPsdkInit(); if ( IsPGPError( err ) ) { // запоминаем место ошибки m_sWhere = "PGPsdkInit()"; goto Exit; } // создаем pgp-контекст err = PGPNewContext( kPGPsdkAPIVersion, &m_context ); if ( IsPGPError( err ) ) { // запоминаем место ошибки m_sWhere = "PGPNewContext()"; goto Exit; } // составляем список опций, общий для всех функций шифрования err = PGPBuildOptionList( m_context, &m_optsEncode, // выводить в текстовом виде ? PGPOArmorOutput( m_context, m_bASCIIoutput ), // входные данные - текст ? PGPODataIsASCII( m_context, m_bASCIIinput ), // комментарий PGPOCommentString( m_context, m_sComment.c_str() ), // версия PGPOVersionString( m_context, m_sVersion.c_str() ), // симметричный алгоритм PGPOCipherAlgorithm( m_context, m_algoID ), // список опций закончен PGPOLastOption( m_context ) ); if ( IsPGPError( err ) ) { // запоминаем место ошибки m_sWhere = "PGPBuildOptionList()"; goto Exit; } // если попали сюда, значит инициализация успешна m_bIsInit = TRUE; Exit: if ( IsPGPError( err ) ) { // на одном из шагов возникла ошибки, // получаем ее описание PGPGetErrorString( err, sizeof( m_sWhat ), m_sWhat ); return FALSE; } return m_bIsInit; } |
Настройка опций шифрования
Изменение доступных опций (строки комментария, версии, форматов входной и выходной информации выполняется однотипно, соответствующей функцией Setxxxxxx, например, изменение комментария:
CSimplePGP::SetComment()// изменение строки - комментария BOOL CSimplePGP::SetComment ( LPCTSTR sComment ) { // запоминаем новый комментарий m_sComment = sComment; // пересобираем лист опций return ReBuildEncodeOptionList(); } |
// пересобрать список опций шифрования в соответствии с новыми настройками BOOL CSimplePGP::ReBuildEncodeOptionList() { if ( !m_bIsInit ) Init(); PGPError err = kPGPError_NoErr; if ( m_optsEncode != NULL ) { // очищаем список опций шифрования PGPFreeOptionList( m_optsEncode ); m_optsEncode = NULL; } // составляем список опций, общий для всех функций шифрования err = PGPBuildOptionList( m_context, &m_optsEncode, // выводить в текстовом виде ? PGPOArmorOutput( m_context, m_bASCIIoutput ), // входные данные - текст ? PGPODataIsASCII( m_context, m_bASCIIinput ), // комментарий PGPOCommentString( m_context, m_sComment.c_str() ), // версия PGPOVersionString( m_context, m_sVersion.c_str() ), // симметричный алгоритм PGPOCipherAlgorithm( m_context, m_algoID ), // список опций закончен PGPOLastOption( m_context ) ); if ( IsPGPError( err ) ) { m_sWhere = "ReBuildEncodeOptionList()"; return FALSE; } return TRUE; } |
Шифрование
Для примера возьмем функцию шифрования данных из буфера в памяти с выводом результатов в файл:
CSimplePGP::EncodeBuff2File()// из памяти в файл, открытый ключ в ресурсах BOOL CSimplePGP::EncodeBuff2File( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буфера LPCTSTR outFileName, // имя выходного файла LPCTSTR resourceName, // имя ресурса c открытыми ключами LPCTSTR resourceType ) // тип ресурса { if ( !m_bIsInit ) Init(); // возвращаемое значение BOOL ret = TRUE; // код ошибки PGPError err = kPGPError_NoErr; // выходной файл PGPFileSpecRef outFileRef = kInvalidPGPFileSpecRef; // набор ключей PGPKeySetRef pubKeySet = kInvalidPGPKeySetRef; // импортируем ключи из ресурсов if ( !ImportKeyFromResorce( resourceName, resourceType, pubKeySet ) ) return FALSE; // готовим выходной файл err = PGPNewFileSpecFromFullPath( m_context, outFileName, &outFileRef ); if ( IsPGPError( err ) ) { m_sWhere = StrPrintf( "PGPNewFileSpecFromFullPath - '%s' ", outFileName ); goto Exit; } // собственно шифрование err = PGPEncode( m_context, // входной буфер PGPOInputBuffer( m_context, ( void* ) inData, ( PGPSize ) dwDataSize ), // выходной файл PGPOOutputFile( m_context, outFileRef ), // шифровать на всех ключах из набора PGPOEncryptToKeySet( m_context, pubKeySet ), // и предварительно сформированный при // инициализации список опций m_optsEncode, // больше опций не будет PGPOLastOption( m_context ) ); if ( IsPGPError( err ) ) { m_sWhere = "PGPEncode()"; } Exit: if ( IsPGPError( err ) ) { ret = FALSE; PGPGetErrorString( err, sizeof( m_sWhat ), m_sWhat ); } // очистка if ( PGPKeySetRefIsValid( pubKeySet ) ) PGPFreeKeySet( pubKeySet ); if ( PGPFileSpecRefIsValid( outFileRef ) ) PGPFreeFileSpec( outFileRef ); return ret; } |
Функция ImportKeyFromResorce() создает набор ключей, используемый в PGPEncode() импортируя его из ресурсов программы:
CSimplePGP::ImportKeyFromResorce()// Загрузка ключей из ресурсов BOOL CSimplePGP::ImportKeyFromResorce( LPCTSTR ResourceName, // имя ресурса с ключами LPCTSTR ResourceType, // тип ресурса PGPKeySetRef& keySet ) // набор ключей, куда импортировать { PGPError err = kPGPError_NoErr; // если нам подсунули валидный keyset, очищаем его if ( PGPKeySetRefIsValid( keySet ) ) PGPFreeKeySet( keySet ); keySet = kInvalidPGPKeySetRef; // переменные для доступа к ресурсам HRSRC pgpKey = NULL; HGLOBAL pRes = NULL; LPVOID pKeyFileInRes = NULL; /* получаем доступ к ключам в ресурсах */ // ищем... pgpKey = FindResource( NULL, ( LPCTSTR ) ResourceName, ( LPCTSTR ) ResourceType ); if ( !pgpKey ) { m_sWhere = StrPrintf( "Не найден ресурс '%s' типа '%s' ", ResourceName, ResourceType ); lstrcpyn( m_sWhat, m_sWhere.c_str(), sizeof( m_sWhat ) ); m_sWhere = "Ошибка ImportKeyFromResorce()"; return FALSE; } // загружаем... pRes = LoadResource( NULL, pgpKey ); if ( !pRes ) { m_sWhere = StrPrintf( "Не удалось загрузить ресурс '%s' типа '%s' ", ResourceName, ResourceType ); lstrcpyn( m_sWhat, m_sWhere.c_str(), sizeof( m_sWhat ) ); m_sWhere = "Ошибка ImportKeyFromResorce()"; return FALSE; } // получаем указатель... pKeyFileInRes = LockResource( pRes ); if ( !pKeyFileInRes ) { m_sWhere = StrPrintf( "Не смогли получить указатель на ресурс '%s' типа '%s' ", ResourceName, ResourceType ); lstrcpyn( m_sWhat, m_sWhere.c_str(), sizeof( m_sWhat ) ); m_sWhere = "Ошибка ImportKeyFromResorce()"; FreeResource( pRes ); return FALSE; } // импортируем ключи из буфера err = PGPImportKeySet( m_context, &keySet, // буфер с ключами PGPOInputBuffer( m_context, pKeyFileInRes, SizeofResource( NULL, pgpKey ) ), // больше опций не будет PGPOLastOption( m_context ) ); // освобождаем ресурсы UnlockResource( pRes ); FreeResource( pRes ); // проверяем на ошибку if ( IsPGPError( err ) ) { m_sWhere = StrPrintf( "PGPImportKeySet( '%s', '%s' ) ", ResourceName, ResourceType ); PGPGetErrorString( err, sizeof( m_sWhat ), m_sWhat ); return FALSE; } return TRUE; } |
Есть также аналогичная функция ImportKeyFromFile() для загрузки ключей из файла.
Деинициализация при завершении работы
Деинициализация включает в себя очистку списка опций, освобождение pgp-контекста и вызов PGPsdkCleanup(). Все эти действия вынесены в деструктор класса:
CSimplePGP::~CSimplePGP// деструктор CSimplePGP::~CSimplePGP( void ) { if ( m_bIsInit ) { if ( m_optsEncode != NULL ) { // очищаем список опций шифрования PGPFreeOptionList( m_optsEncode ); m_optsEncode = NULL; } // освобождаем контекст if ( PGPContextRefIsValid( m_context ) ) PGPFreeContext( m_context ); // PGP library shutdown PGPsdkCleanup(); } } |
Демонстрационная программа PGPTest1
Программа позволяет протестировать возможности PGP SDK по шифрованию данных, реализованные в классе CSimplePGP.
Задавая положение переключателей и имена файлов можно протестировать разные варианты получения входных данных и ключевой информации, а также варианты вывода зашифрованной информации. На поле “”Закрытый” ключ (для подписи)” пока не обращайте внимания, в шифровании он не участвует. В подкаталоге test и в ресурсах программы находится тестовый ключ из комплекта PGP SDK. Пароль для расшифрования данных для этого ключа – test.
Если в качестве места для размещения выходных данных выбрать буфер в памяти, то после нажатия кнопки “Зашифровать” на экране должно появится окно для просмотра содержимого буфера:
Если зашифрованная информация выводится в файл на диске, откроется окно программы Блокнот с загруженным выходным файлом. Сняв галочку "Вывод в формате ASCII" получим файл в бинарном формате:
Расшифровать полученный файл можно, импортировав в программу PGP закрытый ключ из файла test\sectest.asc. Выбрав из контекстного меню файла пункт Decrypt & Verify (при установленной программе PGP) и введя пароль test получим расшифрованный файл:
Продолжение следует …
На очереди расшифровка того, что мы зашифровали, электронная подпись и ее проверка, генерация ключей.
Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.