Использование PGP SDK

Часть 2. Расшифрование и ввод пароля

Автор: Алексей Кирюшкин
The RSDN Group
Опубликовано: 28.11.2002
Версия текста: 1.2

Функции PGP SDK, используемые при расшифровании
PGPDecode
PGPPassphraseIsValid
PGPOPassphrase
PGPOPassphraseBuffer
PGPOKeySetRef
PGPPassphraseDialog
PGPOUIOutputPassphrase
PGPFreeData
PGPOUIWindowTitle
PGPOUIDialogPrompt
PGPOUIParentWindowHandle
PGPsdkUILibInit
PGPsdkUILibCleanup
Поддержка операции расшифрования в классе CSimplePGP
Экспорт секретного ключа из менеджера ключей PGP
Демонстрационная программа PGPTest2
И еще кое-что о секретных и открытых ключах PGP

Класс CSimplePGP – исходные тексты (Win API, STL)
Демонстрационный проект (VC7, WTL7)
Исполняемый файл демонстрационной программы – PGPtest2.exe (для запуска необходимы PGP_SDK.dll, PGPsdkUI.dll и PGPsdkNL.dll)
PGP_SDK.dll
PGPsdkUI.dll
PGPsdkNL.dll

Функции PGP SDK, используемые при расшифровании

Собственно расшифрование данных, зашифрованных открытым ключом, осуществляется при помощи функции

PGPDecode

PGPError PGPDecode(
            PGPContextRef pgpContext,
            PGPOptionListRef firstOption,
            ...,
            PGPOLastOption() );
pgpContextИспользуемый PGP-контекст.
firstOptionПервая опция расшифрования
...Список других необходимых опций
PGPOLastOption()Последняя опция в списке, показывает, что список опций закончен

При передаче в PGPDecode неправильного пароля данная функция не возвращает никакой ошибки, просто не создается выходной файл (при выводе результатов в файл) либо не выделяется память под выходной буфер (при выводе результатов в буфер в памяти).

Проверить соответствие пароля ключу расшифрования можно предварительно вызовом функции

PGPPassphraseIsValid

PGPBoolean PGPPassphraseIsValid(
           PGPKeySetRef key,
           const char *passphrase );
keyСекретный ключ, на соответствие которому проверяется пароль
passphraseУказатель на строку – пароль к секретному ключу

В дополнение к рассмотренным в первой части опциям PGPOInputFile, PGPOOutputFile, PGPOInputBuffer, PGPOOutputBuffer функция PGPDecode использует также следующие опции:

PGPOPassphrase

PGPOptionListRef PGPOPassphrase(
           PGPContextRef pgpContext,
           const char *passphraseBuf );
pgpContextИспользуемый PGP-контекст.
passphraseBufУказатель на строку – пароль к секретному ключу

PGPOPassphraseBuffer

PGPOptionListRef PGPOPassphraseBuffer(
           PGPContextRef pgpContext,
           const void *passphraseBuf,
           PGPSize passphraseLength );
pgpContextИспользуемый PGP-контекст.
passphraseBufУказатель на буфер с паролем к секретному ключу
passphraseLengthРазмер буфера с парольной фразой

PGPOPassphraseBuffer отличается от PGPOPassphrase тем, что буфер с паролем может иметь размер и содержание, не ограниченные синтаксисом строк языка C.

PGPOKeySetRef

PGPOptionListRef PGPOKeySetRef(
           PGPContextRef pgpContext,
           PGPKeySetRef keySet );
pgpContextИспользуемый PGP-контекст.
keySetНабор ключей расшифрования.

Для запроса пароля расшифрования можно использовать диалоговое окно, выводимое функцией

PGPPassphraseDialog

PGPError PGPPassphraseDialog(
           PGPContextRef pgpContext,
           PGPOptionListRef firstOption,
           ...,
           PGPOLastOption() );
pgpContextИспользуемый PGP-контекст.
firstOptionПервая опция в списке
...Список других необходимых опций
PGPOLastOption()Последняя опция в списке, показывает, что список опций закончен

В функцию, выводящую диалог, могут быть переданы следующие опции:

PGPOUIOutputPassphrase

PGPOptionListRef PGPOUIOutputPassphrase(
           PGPContextRef pgpContext,
           char **passphrase );
pgpContextИспользуемый PGP-контекст.
passphraseУказатель на строку, в которой будет размещена полученная парольная фраза.

Если пользователь закроет диалог запроса пароля нажатием на кнопку Cancel или Close passphrase будет установлено в NULL. PGPOUIOutputPassphrase - единственная обязательная опция при вызове диалога ввода пароля. Освобождать память, выделенную при записи парольной фразы в passphrase необходимо вызовом функции

PGPFreeData

PGPError PGPFreeData( void *allocation );
allocationУказатель на освобождаемую область памяти.

PGPOUIWindowTitle

PGPOptionListRef PGPOUIWindowTitle(
           PGPContextRef pgpContext,
           const char *title );
pgpContextИспользуемый PGP-контекст.
titleУказатель на строку, которую надо установить в качестве заголовком для окна запроса пароля.

Если данная опция не применяется при вызове диалога запроса пароля, в заголовке окна устанавливается фраза по умолчанию: "PGP Enter Passphrase".

PGPOUIDialogPrompt

PGPOptionListRef PGPOUIDialogPrompt(
           PGPContextRef pgpContext,
           const char *prompt );
pgpContextИспользуемый PGP-контекст.
promptУказатель на строку, которую надо установить в качестве запроса перед полем для ввода пароля.

Если данная опция не применяется при вызове диалога запроса пароля, в качестве запроса устанавливается фраза по умолчанию: "Passphrase of private key".

PGPOUIParentWindowHandle

PGPOptionListRef PGPOUIParentWindowHandle(
           PGPContextRef pgpContext,
           HWND hwndParent );
pgpContextИспользуемый PGP-контекст.
hwndParentHWND окна, которое надо установить в качестве родительского для окна запроса пароля.

Если данная опция не применяется при вызове диалога запроса пароля, в качестве родительского окна устанавливается десктоп.

Кроме указанных, в PGPPassphraseDialog могут быть также переданы опции PGPOUIDialogOptions, PGPOUIMinimumPassphraseLength, PGPOUIMinimumPassphraseQuality.

Начинать работу с библиотекой пользовательских интерфейсов, PGPsdkUI.dll, нужно с инициализации:

PGPsdkUILibInit

PGPError PGPsdkUILibInit( void );

По завершении работы с библиотекой пользовательских интерфейсов необходимо вызвать функцию очистки -

PGPsdkUILibCleanup

PGPError PGPsdkUILibCleanup( void );

Поддержка операции расшифрования в классе CSimplePGP

Для поддержки операций расшифрования в класс 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 и, забегая вперед, функции PGP SDK не позволяют экспортировать только секретный ключ, но можно экспортировать в текстовый файл пару ключей – секретный и открытый, а затем удалить из него блок текста, соответствующий открытому ключу.

Демонстрационная программа PGPTest2

Программа позволяет протестировать возможности PGP SDK по расшифрованию данных, реализованные в классе CSimplePGP.


Пароль расшифрования для тестовых зашифрованных сообщений – test. После нажатия кнопки "Расшифровать" в зависимости от настроек программы для ввода пароля будет выведен стандартный диалог Windows:


либо "фирменный" диалог PGP SDK:


Если пароль введен правильно, откроется окно с расшифрованным текстом:


При вводе неправильного пароля выводится соответствующее сообщение.

И еще кое-что о секретных и открытых ключах PGP

Программа PGP хранит ключи в связке из двух файлов – pubring.pkr и secring.skr. Функции PGP SDK, и, соответственно, класс CSimplePGP могут работать с ключами непосредственно из этих файлов. Однако, имейте ввиду,

ПРЕДУПРЕЖДЕНИЕ

Не смотря на название файл secring.skr содержит не только секретные ключи.

В этом легко убедиться – тестовая программа из первой части, PGPtest1, позволяет зашифровать что-либо, используя только secring.skr. Тоже касается и собственно программы PGP – если заменить pubring.pkr на файл с таким же названием, но нулевого размера, в списке менеджера ключей останутся только имеющиеся у вас секретные ключи, но возможность зашифровать что-либо используя только эти ключи все равно остается.


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