Функции PGP SDK, используемые при наложении цифровой подписи
Собственно наложение цифровой подписи осуществляется функцией PGPEncode, описанной в первой части. В дополнение к уже описанным ранее в данной операции участвуют следующие опции:
Устанавливается в TRUE, если не нужно шифровать открытый текст, на который накладывается подпись.
Данную опцию можно устанавливать только при наложении подписи на текстовую информацию, т.к. при установке данной настройки в TRUE автоматически устанавливаются в TRUE опции PGPOArmorOutput и PGPODataIsASCII. Подписываемый текст и собственно подпись находятся в одном файле (буфере памяти), разделенные соответствующими текстовыми метками:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Сим заверяем вас, глубокоуважаемый Заказчик, в нашем совершеннейшем
почтении и безграничном уважении к Вам и нуждам Вашего Бизнеса. По поводу
некоторой задержки сроков реализации вашего Заказа, а также превышения
предполагаемой суммы затрат на 143%, нижайше просим перечитать пункт 4.7.2
Типового Договора На Разработку Программного Решения, "Риски проекта".
-----BEGIN PGP SIGNATURE-----
Version: CSimplePGP v3.0
Comment: no comment
iQA/AwUBPvPF3sJmEIGmukqAEQJJIACgnQA6Ydj8aGzR5Aln6nUVQ0skZakAmgJr
qBv2VMWGGjjuF6qYKTo6URTy
=ZOZa
-----END PGP SIGNATURE-----
Последняя опция в списке, показывает, что список опций закончен
При передаче в PGPEncode указывает на необходимость создания цифровой подписи в отдельном файле, при этом не требуется передавать никаких дополнительных опций в PGPODetachedSig. При передаче в PGPDecode указывает на источник, в котором содержится цифровая подпись для верификации. В этом случае необходимо передать в PGPODetachedSig одну из опций PGPOInputBuffer, PGPOInputFile или PGPOInputFileFSSpec для связи с источником цифровой подписи.
При наложении цифровой подписи с опцией PGPODetachedSig на основе подписываемой информации создается хеш, который затем шифруется секретным ключом (с запросом пароля секретного ключа), полученная таким образом подпись помещается в отдельный файл (либо буфер). Если дополнительно задана опция PGPOArmorOutput (выходные данные в виде ASCII), подпись будет иметь следующий вид:
Если PGPOArmorOutput не установлена, подпись создается в бинарном формате.
При верификации подписи она будет расшифрована открытым ключом подписавшего и извлеченное из нее значение хеша сравнено с вновь рассчитанным для подписанной информации.
Если при наложении цифровой подписи опция PGPODetachedSig не была установлена, секретным ключом подписывающего шифруется вся подписываемая информация, соответственно результат будет иметь вид, близкий к следующему:
Последняя опция в списке, показывает, что список опций закончен
В списке опций шифрования, требуемых для PGPOSignWithKey должна быть передана одна из опций PGPOPasskeyBuffer, PGPOPassphrase, PGPOPassphraseBuffer, задающих пароль для секретного ключа (см. вторую часть статьи).
В отличии от операций шифрования, которые выполняются для набора ключей, наложение цифровой подписи должно производится только с одним определенным ключом. Для выбора этого ключа из набора на него нужно наложить фильтр по необходимому критерию. PGP SDK предоставляет около трех десятков функций для задания фильтра по дате создания ключа, номеру ключа, алгоритму шифрования, email хозяина ключа и т.д. Функция PGPNewUserIDStringFilter создает фильтр по строке – идентификатору хозяина ключа. В качестве критерия выборки PGPMatchCriterion могут использоваться значения из следующего перечисления (pgpKeys.h):
enum PGPMatchCriterion_
{
kPGPMatchDefault = 1, /* тоже что и kPGPMatchEqual */
kPGPMatchEqual = 1, /* строгое равенство искомой и заданной для сравнения величин */
kPGPMatchGreaterOrEqual = 2, /* искомая величина >= заданной для сравнения */
kPGPMatchLessOrEqual = 3, /* искомая величина <= заданной для сравнения */
kPGPMatchSubString = 4, /* искомая строка содержится в заданной для сравнения */
PGP_ENUM_FORCE( PGPMatchCriterion_ )
};
PGPENUM_TYPEDEF( PGPMatchCriterion_, PGPMatchCriterion );
По окончании использования (до вызова PGPFreeContext) созданный фильтр должен быть освобожден вызовом функции
Результат операции – отсортированный список ключей
Функция упорядочивает заданный набор ключей согласно заданному критерию сортировки. Критерий сортировки задается указанием одного из следующих параметров (pgpKeys.h):
Данная опция не действует при подписи DSS ключом (DSS - Digital Signature Standard от NIST (National Institute for Standards and Technology USA)), в этом случае всегда используется SHA-1.
Поддержка операции наложения цифровой подписи в классе CSimplePGP
Для поддержки операций наложения цифровой подписи в класс CSimplePGP добавлены следующие члены:
public:
// установка - подпись без шифрования исходного текста, в том же файле
BOOL SetClearSigning ( BOOL bClearsign );
// установка - подпись в отдельном файле
BOOL SetDetachedSig ( BOOL bDetachedSig );
// установка - строка - идентификатор хозяина ключаvoid SetSignerIDstring ( string signerID );
// алгоритм хеширования
BOOL SetHashAlgorithm( PGPHashAlgorithm hashID );
// ЦИФРОВАЯ ПОДПИСЬ// подписываем файл, секретный ключ тоже в файле
BOOL SignFile2File( LPCTSTR inFileName, // имя входного файла для подписания
LPCTSTR outFileName, // имя выходного файла
LPCTSTR keyFileName ); // имя файла с секретным ключом// подписываем файл, секретный ключ в ресурсах
BOOL SignFile2File( LPCTSTR inFileName, // имя входного файла
LPCTSTR outFileName, // имя выходного файла
LPCTSTR resourceName, // имя ресурса c секретным ключом
LPCTSTR resourceType ); // тип ресурса// данные - из памяти, подпись - в файл, секретный ключ тоже в файле
BOOL SignBuff2File( const VOID* inData, // указатель на буфер с данными
DWORD dwDataSize, // размер буффера
LPCTSTR outFileName, // имя выходного файла
LPCTSTR keyFileName ); // имя файла с секретным ключом// данные - из памяти, подпись - в файл, секретный ключ в ресурсах
BOOL SignBuff2File( const VOID* inData, // указатель на буфер с данными
DWORD dwDataSize, // размер буфера
LPCTSTR outFileName, // имя выходного файла
LPCTSTR resourceName, // имя ресурса c секретным ключом
LPCTSTR resourceType ); // тип ресурса// данные - из файла, подпись - в буфер, секретный ключ в файле.
BOOL SignFile2Buff( LPCTSTR inFileName, // имя входного файла
LPBYTE& OutData, // указатель на буфер для данных
DWORD& BuffSize, // размер буфера
LPCTSTR keyFileName ); // имя файла с секретным ключом// данные - из файла, подпись - в буфер, секретный ключ в ресурсах
BOOL SignFile2Buff( LPCTSTR inFileName, // имя входного файла
LPBYTE& OutData, // указатель на буфер для данных
DWORD& BuffSize, // размер буфера
LPCTSTR resourceName, // имя ресурса c секретным ключом
LPCTSTR resourceType ); // тип ресурса// данные - из памяти, подпись - в память, секретный ключ в файле
BOOL SignBuff2Buff( const VOID* inData, // указатель на буфер с данными
DWORD dwDataSize, // размер буффера
LPBYTE& OutData, // указатель на буфер для данных
DWORD& BuffSize, // размер буфера
LPCTSTR keyFileName ); // имя файла с секретным ключом// данные - из памяти, подпись - в память, секретный ключ в ресурсах
BOOL SignBuff2Buff( const VOID* inData, // указатель на буфер с данными
DWORD dwDataSize, // размер буфера
LPBYTE& OutData, // указатель на буфер для данных
DWORD& BuffSize, // размер буфера
LPCTSTR resourceName, // имя ресурса c секретным ключом
LPCTSTR resourceType ); // тип ресурса
В качестве примера реализации операции цифровой подписи рассмотрим создание в буфере подписи для файла с данными:
SignFile2Buff()
// данные - из файла, подпись - в буфер, секретный ключ в ресурсах// В функции выделяется память под буфер// OutData, не забудьте освободить ее после// использования буфера, размер выделенной// памяти после выполнения функции// находится в BuffSize
BOOL CSimplePGP::SignFile2Buff( LPCTSTR inFileName, // имя входного файла
LPBYTE& OutData, // указатель на буфер для данных
DWORD& BuffSize, // размер буфера
LPCTSTR resourceName, // имя ресурса c секретным ключом
LPCTSTR resourceType ) // тип ресурса
{
if ( !m_bIsInit )
Init();
BOOL ret = TRUE;
// код ошибки
PGPError err = kPGPError_NoErr;
// входной файл
PGPFileSpecRef inFileRef = kInvalidPGPFileSpecRef;
// набор ключей из ресурсов
PGPKeySetRef secKeysSet = kInvalidPGPKeySetRef;
// импортируем ключи из ресурсовif ( !ImportKeySetFromResorce( resourceName, resourceType, secKeysSet ) )
return FALSE;
// извлекаем из набора ключ для подписания
PGPKeyRef secKeyRef = GetFirstKeyFromSet( secKeysSet );
if ( secKeyRef == kInvalidPGPKeyRef )
{
ret = FALSE;
goto Clear;
}
// готовим входной файл
err = PGPNewFileSpecFromFullPath( m_context, inFileName, &inFileRef );
if ( IsPGPError( err ) )
{
m_sWhere = StrPrintf( "PGPNewFileSpecFromFullPath - '%s' ", inFileName );
goto Exit;
}
// начальный размер буффера// заведомо маленький, после первого вызова PGPEncode в// параметре PGPOOutputBuffer будет передано действительно требуемое// значение размера буфера// ( 0 в качестве начального значения не проходит, поэтому 1 )
BuffSize = 1;
// собственно шифрованиеdo
{
delete[] OutData;
// Выделенную здесь память надо освобождать по окончании использования// выходного буфера
OutData = new BYTE[ BuffSize ];
// собственно подпись
err = PGPEncode( m_context,
// входной файлPGPOInputFile( m_context, inFileRef ),
// выходной буферPGPOOutputBuffer( m_context,
( void* ) OutData,
( PGPSize ) BuffSize,
( PGPSize * ) & BuffSize ),
// подписать PGPOSignWithKey( m_context, secKeyRef, m_optsPassphrase, PGPOLastOption( m_context ) ),
// и предварительно сформированный при// инициализации список опций
m_optsSigning,
// больше опций не будетPGPOLastOption( m_context ) );
}
while ( err == kPGPError_OutputBufferTooSmall );
if ( IsPGPError( err ) )
{
m_sWhere = "PGPEncode()";
}
Exit:
if ( IsPGPError( err ) )
{
ret = FALSE;
// получаем описание ошибкиPGPGetErrorString( err, sizeof( m_sWhat ), m_sWhat );
}
Clear:
// очисткаif ( PGPKeySetRefIsValid( secKeysSet ) )
PGPFreeKeySet( secKeysSet );
if ( PGPFileSpecRefIsValid( inFileRef ) )
PGPFreeFileSpec( inFileRef );
return ret;
}
Как видно из исходного текста порядок операций практически полностью совпадает с операцией шифрования файла, рассмотренной в первой части статьи, за исключением опций, передаваемых в PGPEncode и функции GetFirstKeyFromSet, необходимой для получения одного единственного ключа из набора:
Программа позволяет протестировать возможности PGP SDK по наложению цифровой подписи, реализованные в классе CSimplePGP.
Задавая положение переключателей и имена файлов можно протестировать разные варианты получения входных данных и ключевой информации, а также варианты вывода подписанной информации. В подкаталоге test и в ресурсах программы находятся тестовый ключ из комплекта PGP SDK (2048/1024 DH/DSS, идентификатор test@pgp.com) и тестовый ключ RSA ( идентификатор rsakey). Пароль для обоих ключей одинаковый – test.
ПРИМЕЧАНИЕ
Как уже было сказано выше, настройка алгоритма хеширования действует только для ключа RSA.
Если в качестве места для размещения выходных данных выбрать буфер в памяти, то после нажатия кнопки “Подписать” на экране должно появится окно для просмотра содержимого буфера:
Верифицировать наложенную цифровую подпись можно с помощью программы PGP, предварительно импортировав в менеджер ключей тестовый ключ из комплекта PGP SDK и тестовый ключ RSA (test\rsakey.asc).
Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы
то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских
прав.