Класс CSimplePGP – исходные тексты (Win API, STL)
Демонстрационный проект (VC7, WTL7)
Исполняемый файл демонстрационной программы – PGPtest2.exe (для запуска необходимы PGP_SDK.dll, PGPsdkUI.dll и PGPsdkNL.dll)
PGP_SDK.dll
PGPsdkUI.dll
PGPsdkNL.dll
Собственно расшифрование данных, зашифрованных открытым ключом, осуществляется при помощи функции
PGPError PGPDecode( PGPContextRef pgpContext, PGPOptionListRef firstOption, ..., PGPOLastOption() ); |
pgpContext | Используемый PGP-контекст. |
---|---|
firstOption | Первая опция расшифрования |
... | Список других необходимых опций |
PGPOLastOption() | Последняя опция в списке, показывает, что список опций закончен |
При передаче в PGPDecode неправильного пароля данная функция не возвращает никакой ошибки, просто не создается выходной файл (при выводе результатов в файл) либо не выделяется память под выходной буфер (при выводе результатов в буфер в памяти).
Проверить соответствие пароля ключу расшифрования можно предварительно вызовом функции
PGPBoolean PGPPassphraseIsValid( PGPKeySetRef key, const char *passphrase ); |
key | Секретный ключ, на соответствие которому проверяется пароль |
---|---|
passphrase | Указатель на строку – пароль к секретному ключу |
В дополнение к рассмотренным в первой части опциям PGPOInputFile, PGPOOutputFile, PGPOInputBuffer, PGPOOutputBuffer функция PGPDecode использует также следующие опции:
PGPOptionListRef PGPOPassphrase( PGPContextRef pgpContext, const char *passphraseBuf ); |
pgpContext | Используемый PGP-контекст. |
---|---|
passphraseBuf | Указатель на строку – пароль к секретному ключу |
PGPOptionListRef PGPOPassphraseBuffer( PGPContextRef pgpContext, const void *passphraseBuf, PGPSize passphraseLength ); |
pgpContext | Используемый PGP-контекст. |
---|---|
passphraseBuf | Указатель на буфер с паролем к секретному ключу |
passphraseLength | Размер буфера с парольной фразой |
PGPOPassphraseBuffer отличается от PGPOPassphrase тем, что буфер с паролем может иметь размер и содержание, не ограниченные синтаксисом строк языка C. |
PGPOptionListRef PGPOKeySetRef( PGPContextRef pgpContext, PGPKeySetRef keySet ); |
pgpContext | Используемый PGP-контекст. |
---|---|
keySet | Набор ключей расшифрования. |
Для запроса пароля расшифрования можно использовать диалоговое окно, выводимое функцией
PGPError PGPPassphraseDialog( PGPContextRef pgpContext, PGPOptionListRef firstOption, ..., PGPOLastOption() ); |
pgpContext | Используемый PGP-контекст. |
---|---|
firstOption | Первая опция в списке |
... | Список других необходимых опций |
PGPOLastOption() | Последняя опция в списке, показывает, что список опций закончен |
В функцию, выводящую диалог, могут быть переданы следующие опции:
PGPOptionListRef PGPOUIOutputPassphrase(
PGPContextRef pgpContext,
char **passphrase );
|
pgpContext | Используемый PGP-контекст. |
---|---|
passphrase | Указатель на строку, в которой будет размещена полученная парольная фраза. |
Если пользователь закроет диалог запроса пароля нажатием на кнопку Cancel или Close passphrase будет установлено в NULL. PGPOUIOutputPassphrase - единственная обязательная опция при вызове диалога ввода пароля. Освобождать память, выделенную при записи парольной фразы в passphrase необходимо вызовом функции
PGPError PGPFreeData( void *allocation );
|
allocation | Указатель на освобождаемую область памяти. |
---|
PGPOptionListRef PGPOUIWindowTitle( PGPContextRef pgpContext, const char *title ); |
pgpContext | Используемый PGP-контекст. |
---|---|
title | Указатель на строку, которую надо установить в качестве заголовком для окна запроса пароля. |
Если данная опция не применяется при вызове диалога запроса пароля, в заголовке окна устанавливается фраза по умолчанию: "PGP Enter Passphrase".
PGPOptionListRef PGPOUIDialogPrompt( PGPContextRef pgpContext, const char *prompt ); |
pgpContext | Используемый PGP-контекст. |
---|---|
prompt | Указатель на строку, которую надо установить в качестве запроса перед полем для ввода пароля. |
Если данная опция не применяется при вызове диалога запроса пароля, в качестве запроса устанавливается фраза по умолчанию: "Passphrase of private key".
PGPOptionListRef PGPOUIParentWindowHandle( PGPContextRef pgpContext, HWND hwndParent ); |
pgpContext | Используемый PGP-контекст. |
---|---|
hwndParent | HWND окна, которое надо установить в качестве родительского для окна запроса пароля. |
Если данная опция не применяется при вызове диалога запроса пароля, в качестве родительского окна устанавливается десктоп.
Кроме указанных, в PGPPassphraseDialog могут быть также переданы опции PGPOUIDialogOptions, PGPOUIMinimumPassphraseLength, PGPOUIMinimumPassphraseQuality.
Начинать работу с библиотекой пользовательских интерфейсов, PGPsdkUI.dll, нужно с инициализации:
PGPError PGPsdkUILibInit( void ); |
По завершении работы с библиотекой пользовательских интерфейсов необходимо вызвать функцию очистки -
PGPError PGPsdkUILibCleanup( void );
|
Для поддержки операций расшифрования в класс CSimplePGP добавлены следующие члены:
protected: // Опции расшифрования общие для всех вариантов PGPOptionListRef m_optsDecode; public: // установка парольной фразы для расшифрования BOOL SetPassphrase ( LPCTSTR sPassphrase ); #ifdef _WITH_PGPUI_ // запрос пароля у пользователя c использованием PGPsdkUI.dll BOOL AskPassphrase( HWND hwndParent ); #endif // РАСШИФРОВЫВАНИЕ // из файла в файл, секретный ключ тоже в файле BOOL DecodeFile2File( LPCTSTR inFileName, // имя входного файла LPCTSTR outFileName, // имя выходного файла LPCTSTR keyFileName ); // имя файла с секретным ключом // из файла в файл, секретный ключ в ресурсах BOOL DecodeFile2File( LPCTSTR inFileName, // имя входного файла LPCTSTR outFileName, // имя выходного файла LPCTSTR resourceName, // имя ресурса c секретным ключом LPCTSTR resourceType ); // тип ресурса // из памяти в файл, секретный ключ тоже в файле BOOL DecodeBuff2File( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буффера LPCTSTR outFileName, // имя выходного файла LPCTSTR keyFileName ); // имя файла с секретным ключом // из памяти в файл, секретный ключ в ресурсах BOOL DecodeBuff2File( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буфера LPCTSTR outFileName, // имя выходного файла LPCTSTR resourceName, // имя ресурса c секретным ключом LPCTSTR resourceType ); // тип ресурса // из файла в буфер, секретный ключ в файле. BOOL DecodeFile2Buff( LPCTSTR inFileName, // имя входного файла LPBYTE& OutData, // указатель на буфер для данных DWORD& BuffSize, // размер буфера LPCTSTR keyFileName ); // имя файла с секретным ключом // из файла в буфер, секретный ключ в ресурсах BOOL DecodeFile2Buff( LPCTSTR inFileName, // имя входного файла LPBYTE& OutData, // указатель на буфер для данных DWORD& BuffSize, // размер буфера LPCTSTR resourceName, // имя ресурса c секретным ключом LPCTSTR resourceType ); // тип ресурса // из памяти в память, секретный ключ в файле BOOL DecodeBuff2Buff( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буффера LPBYTE& OutData, // указатель на буфер для данных DWORD& BuffSize, // размер буфера LPCTSTR keyFileName ); // имя файла с секретным ключом // из памяти в память, секретный ключ в ресурсах BOOL DecodeBuff2Buff( const VOID* inData, // указатель на буфер с данными DWORD dwDataSize, // размер буфера LPBYTE& OutData, // указатель на буфер для данных DWORD& BuffSize, // размер буфера LPCTSTR resourceName, // имя ресурса c секретным ключом LPCTSTR resourceType ); // тип ресурса |
Реализация и применение функций DecodeXXXXXXX аналогичны описанным в первой части функциям шифрования – EncodeXXXXXX. Подробности можно посмотреть в исходных текстах CSimplePGP.
Функция SetPassphrase() осуществляет непосредственное внесение парольной фразы в список опций используемых при расшифровании:
// установка парольной фразы для расшифрования BOOL CSimplePGP::SetPassphrase ( LPCTSTR sPassphrase ) { if ( !m_bIsInit ) Init(); PGPError err = kPGPError_NoErr; if ( m_optsDecode != NULL ) { // очищаем список опций шифрования PGPFreeOptionList( m_optsDecode ); m_optsDecode = NULL; } // составляем список опций, общий для всех функций шифрования err = PGPBuildOptionList( m_context, &m_optsDecode, // парольная фраза PGPOPassphrase( m_context, sPassphrase ), // список опций закончен PGPOLastOption( m_context ) ); if ( IsPGPError( err ) ) { m_sWhere = "SetPassphrase()"; return FALSE; } return TRUE; } |
В функцию Init() добавлена инициализация библиотеки пользовательских интерфейсов PGPSDK:
#ifdef _WITH_PGPUI_ #include "pgpUserInterface.h" #pragma comment(lib,"pgpsdkui.lib") #endif // инициализация BOOL CSimplePGP::Init() { ... ... #ifdef _WITH_PGPUI_ err = PGPsdkUILibInit(); if ( IsPGPError( err ) ) { // запоминаем место ошибки m_sWhere = "PGPsdkUILibInit()"; goto Exit; } #endif ... ... } |
А в деструктор – вызов функции очистки
CSimplePGP::~CSimplePGP( void ) { if ( m_bIsInit ) { if ( m_optsEncode != NULL ) { // очищаем список опций шифрования PGPFreeOptionList( m_optsEncode ); m_optsEncode = NULL; } if ( m_optsDecode != NULL ) { // очищаем список опций расшифрования PGPFreeOptionList( m_optsDecode ); m_optsEncode = NULL; } // освобождаем контекст if ( PGPContextRefIsValid( m_context ) ) PGPFreeContext( m_context ); // PGP library shutdown #ifdef _WITH_PGPUI_ PGPsdkUILibCleanup(); #endif PGPsdkCleanup(); } } |
Следует, пожалуй, отдельно остановится на операции запроса пароля расшифрования у пользователя. Функция AskPassphrase() использует для вывода диалога запроса пароля соответствующую функцию PGPSDK:
#ifdef _WITH_PGPUI_ // запрос пароля у пользователя BOOL CSimplePGP::AskPassphrase( HWND hwndParent ) { char * passphrase; BOOL ret = TRUE; // код ошибки PGPError err = kPGPError_NoErr; err = PGPPassphraseDialog( m_context, PGPOUIOutputPassphrase( m_context, &passphrase ), PGPOUIWindowTitle( m_context, "Введите пароль" ), PGPOUIDialogPrompt( m_context, "Пароль секретного ключа:" ), PGPOUIParentWindowHandle( m_context, hwndParent ), PGPOLastOption( m_context ) ); if ( IsPGPError( err ) ) { // запоминаем место ошибки m_sWhere = "PGPPassphraseDialog()"; ret = FALSE; // получаем описание ошибки PGPGetErrorString( err, sizeof( m_sWhat ), m_sWhat ); } if ( passphrase ) { SetPassphrase( passphrase ); PGPFreeData ( ( void* ) passphrase ); passphrase = NULL; } else { ret = FALSE; } return ret; } #endif |
Т.к. данные возможности нужны далеко не всегда, а требуют дополнительно наличия PGPsdkUI.dll и PGPsdkNL.dll, они внесены в класс CSimplePGP опционально и зависят от определения в проекте макроса _WITH_PGPUI_. Если вам интересно, зачем вообще нужно использовать для ввода пароля специальный диалог из PGPSDK, запустите демонстрационную программу вместе с каким-либо логгером, ведущим протокол всех нажатых клавиш (см., например HookDump), введите пароль в стандартном диалоге Windows и в диалоге из PGPSDK и посмотрите записанный протокол.
Менеджер ключей PGP и, забегая вперед, функции PGP SDK не позволяют экспортировать только секретный ключ, но можно экспортировать в текстовый файл пару ключей – секретный и открытый, а затем удалить из него блок текста, соответствующий открытому ключу.
Программа позволяет протестировать возможности PGP SDK по расшифрованию данных, реализованные в классе CSimplePGP.
Пароль расшифрования для тестовых зашифрованных сообщений – test. После нажатия кнопки "Расшифровать" в зависимости от настроек программы для ввода пароля будет выведен стандартный диалог Windows:
либо "фирменный" диалог PGP SDK:
Если пароль введен правильно, откроется окно с расшифрованным текстом:
При вводе неправильного пароля выводится соответствующее сообщение.
Программа PGP хранит ключи в связке из двух файлов – pubring.pkr и secring.skr. Функции PGP SDK, и, соответственно, класс CSimplePGP могут работать с ключами непосредственно из этих файлов. Однако, имейте ввиду,
ПРЕДУПРЕЖДЕНИЕ Не смотря на название файл secring.skr содержит не только секретные ключи. |
В этом легко убедиться – тестовая программа из первой части, PGPtest1, позволяет зашифровать что-либо, используя только secring.skr. Тоже касается и собственно программы PGP – если заменить pubring.pkr на файл с таким же названием, но нулевого размера, в списке менеджера ключей останутся только имеющиеся у вас секретные ключи, но возможность зашифровать что-либо используя только эти ключи все равно остается.