КриптоПро CSP 3.0

127018, Москва, Улица Образцова, 38
Телефон: (095) 933 1168
Факс: (095) 933 1168
http://www.CryptoPro.ru
E-mail: info@CryptoPro.ru

Средство Криптографической Защиты Информации

КриптоПро CSP

Версия 3.0

Руководство программиста

ЖТЯИ.00015-01 90 03

Модуль сетевой аутентификации КриптоПро TLS

© OOO "Крипто-Про", 2000-2004. Все права защищены.
Авторские права на средство криптографической защиты информации КриптоПро TLS и эксплуатационную документацию зарегистрированы в Российском агентстве по патентам и товарным знакам (Роспатент). Свидетельство об официальной регистрации программ для ЭВМ № 2001610275 от 14 марта 2001 года.
Документ входит в комплект поставки программного обеспечения КриптоПРО TLS, и на него распространяются все условия лицензионного соглашения. Без специального письменного разрешения OOO "Крипто-Про" документ или его часть в электронном или печатном виде не могут быть скопированы и переданы третьим лицам с коммерческой целью.


Введение

КриптоПро CSP 3.0

ЖТЯИ.00005-01 30 01

© OOO "Крипто-Про" 2000-2004

Средство
Криптографической
Защиты
Информации

Модуль сетевой аутентификации КриптоПро TLS

Модуль сетевой аутентификации КриптоПро TLS разработан в соответствии с криптографическим интерфейсом корпорации Microsoft - Security Service Provider Interface (SSPI).

Модуль КриптоПро TLS используется в составе СКЗИ КриптоПро CSP, реализующем российские криптографические алгоритмы, и встраивается в системы Microsoft в качестве дополнения к стандартному модулю Schannel.

Область применения

Интеграция КриптоПро TLS с операционной системой Windows позволила использовать технологии Microsoft с российскими криптографическими алгоритмами для аутентификации и защиты каналов связи по протоколу TLS/SSL (RFC 2246), на основе Инфраструктуры Открытых Ключей (Public Key Infrastructure), соответствующей международным рекомендациям X.509, RFC 2459 и Федеральному закону "Об электронной цифровой подписи".

Использование КриптоПро CSP позволяет решить сразу несколько задач:

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

К стандартным приложениям, которые теперь могут использовать российские алгоритмы для защиты соединений, относятся:

  • Веб-обозреватель - Microsoft Internet Explorer
  • Веб-сервер - Microsoft Internet Information Server
  • Firewall - Microsoft Internet Security and Acceleration Server
  • Электронная почта - Microsoft Outlook
  • Электронная почта - Microsoft Outlook Express

Аннотация

Краткое описание КриптоПро TLS.

Архитектура

Архитектура пакетов безопасности операционной системы Windows (SSPI).

Руководство программиста

Описание функций, структур, переменных КриптоПро TLS.

Обратная связь

Связь с разработчиком.
Поддержка.
Конференция.


Модуль сетевой аутентификации КриптоПро TLS

Основные функции КриптоПро TLS

Основные функции, реализуемые КриптоПро TLS:

  • Две схемы аутентификации с использованием обмена ключей по алгоритму Диффи-Хэллмана и хэширования в соответствии с ГОСТ Р 34.11-94.
    • односторонняя - анонимный клиент, аутентифициремый сервер;
    • двухстороняя - аутентифициремые клиент и сервер;
    В случае аутентификации клиента на ключе подписи применяются алгоритмы выработки и проверки электронной цифровой подписи в соответствии с ГОСТ Р 34.10-94 или ГОСТ Р 34.10-2001.
  • шифрование соединения в соответствии с ГОСТ 28147-89 ("Системы обработки информации. Защита криптографическая.");
  • имитозащита передаваемых данных в соответствии с ГОСТ 28147-89;

Операционные системы

КриптоПро TLS функционирует в следующих операционных системах (ОС):

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.

Интеграция с прикладным программным обеспечением

О способах использования КриптоПро TLS смотрите в разделе Работа с пакетами безопасности операционной системы Windows.

Крипто-Про SSPI Версия: 3.0 Сборка 3293


Работа с пакетами безопасности Windows

При разработке модуля КриптоПро TLS учитывалась существующая в операционной системе Windows архитектура пакетов безопасности. В соответствии с этой архитектурой протоколы защищенного обмена реализуются через специфицированный Microsoft интерфейс - Security Service Provider Interface (SSPI) World Wide Web link .

В соответствии с существующей в операционной системе Windows архитектуре использования пакетов безопасности КриптоПро TLS возможно использовать как в стандартном программном обеспечении Microsoft, так и встраивать в разрабатываемое прикладное программное обеспечение.

Использование в программном обеспечении Microsoft

  • Электронная почта - Электронная почта - Microsoft Outlook 98, 2000, XP World Wide Web link ;
  • Электронная почта - Электронная почта - Microsoft Outlook Express, входящая в состав Internet Explorer версии 5.0 или выше World Wide Web link ;
  • Защищенный доступ к Web - Internet Explorer версии 5.0 или выше.
  • Защищенный Web-сервер - Internet Information Service 5.0(4.0).

Встраивание модуля КриптоПро TLS

Кроме использования протокола TLS в интерфейсе Internet Explorer, прикладное программное обеспечение может использовать протокол TLS для аутентификации и защиты данных, передаваемых по собственным протоколам на основе TCP/IP и HTTPS.

Примеры встраивания имеются в Тестовое ПО разработки КриптоПро World Wide Web link (файлы WebClient.c, WebServer.c, tls.c), а также в составе Microsoft Platform SDK (<PSDKDir>\Samples\WinBase\Security\SSL).

Инициализация библиотеки SSP

Производится загрузка библиотеки Secur32.dll, Security.dll при работе с операционными системами Windows NT 4.0, Windows 2000, Windows ХР; библиотеки schannel.dll - при работе с операционными системами Windows 95, Windows 95 OSR2, Windows 98, Windows 98 SE, Windows ME.

С помощью функции GetProcAddress получается указатель на функцию InitSecurityInterfaceA (InitSecurityInterfaceW в случае компиляции с Unicode).

Вызовом функции InitSecurityInterfaceA (InitSecurityInterfaceW в случае компиляции с Unicode) получается таблица функций SSPI.

Заполняется структура SCHANNEL_CRED. Поля этой структуры должны быть нулевыми, кроме:

SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
SchannelCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
Для сервера и не анонимного клиента заполняются также поля:

SchannelCred.cCreds     = 1;
SchannelCred.paCred     = &pCertContext.

Примечание. Контекст сертификата pCertContext должен содержать ссылку на секретный ключ.

Производится вызов функции создания Credentials: AcquireCredentialsHandle с передачей ей структуры SCHANNEL_CRED и имени пакета - UNISP_NAME ("Microsoft Unified Security Protocol Provider").

Инициализация соединения клиентом производится вызовом InitializeSecurityContext без входного буфера и сервером - вызовом AcceptSecurityContext, после чего идет обычный цикл Handshake.

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

Для получения сертификата удаленной стороны вызывается функция QueryContextAttributes с аргументом SECPKG_ATTR_REMOTE_CERT_CONTEXT.

Для построения цепочки сертификатов рекомендуется использование функции CertGetCertificateChain, описанную в MSDN/Platform SDK/Security, (с флагами проверки, соответствующими выбранному уровню безопасности. Рекомендуется использовать флаг CERT_CHAIN_CACHE_END_CERT | CERT_CHAIN_REVOCATION_CHECK_CHAIN.

Цепочка сертификатов проверяется функцией CertVerifyCertificateChainPolicy, описанной там же, с аргументом pszPolicy, равным OIDCERT_CHAIN_POLICY_SSL, и аргументом pPolicyPara, заполненным следующим образом:

ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
polHttps.cbStruct           = sizeof(HTTPSPolicyCallbackData);
polHttps.dwAuthType         = AUTHTYPE_SERVER;
polHttps.fdwChecks          = 0;
polHttps.pwszServerName     = pwszServerName;
memset(&PolicyPara, 0, sizeof(PolicyPara));
PolicyPara.cbSize            = sizeof(PolicyPara);
PolicyPara.pvExtraPolicyPara = &polHttps;
Необходимо, чтобы для каждого сертификата в цепочке pCertContext->pCertInfo->SubjectPublickeyInfo->Algoritm->pszObjId заканчивалась на szOID_GR3410.

Вызывается функция QueryContextAttributes с аргументом ulAttribute, равным SECPKG_ATTR_CONNECTION_INFO, для получения параметров соединения и их проверки на выполнение условий:

ConnectionInfo.dwProtocol == SP_PROT_TLS1_CLIENT
ConnectionInfo.aiCipher == CALG_G28147
ConnectionInfo.aiHash == CALG_GR3411
ConnectionInfo.aiExch == CALG_DH_EX_EPHEM || ConnectionInfo.aiExch == CALG_DH_EX_SF || ConnectionInfo.aiExch == CALG_DH_EL_EPHEM || ConnectionInfo.aiExch == CALG_DH_EL_SF
Цикл шифрования/рас Для получения параметров соединения шифрования данных реализуется с помощью функций EncryptMessage/DecryptMessage.

Примечание. Должна быть обеспечена корректная обработка кодов возврата функций SSPI. При этом следует учитывать, что требуется разная обработка в зависимости от того, является код возврата кодом успешного выполнения функции, кодом не фатальной ошибки, не требующей разрыва соединения, кодом фатальной ошибки, требующей разрыва соединения. Все необрабатываемые коды возврата ошибок должны приводить к разрыву соединения.

Завершение сессии

Корректное завершение сессии осуществляется вызовом функции ApplyControlToken.

Требование безопасности

Применение КриптоПро TLS допускается только при использовании открытых ключей сервера и клиента, сертифицированных доверенным центром сертификации.

Заключение

Модуль КриптоПро TLS позволяет использовать для аутентификации и защиты соединений стойкие сертифицированные средства криптографической защиты информации в составе обширного инструментария и программного обеспечения корпорации Microsoft, на основе Инфраструктуры Открытых Ключей (Public Key Infrastructure), соответствующей международным рекомендациям X.509, RFC 2459.


Функции КриптоПро TLS

Этот набор функций соответствует интерфейсу SSPI.

  • Функции работы с пакетами безопасности
  • Функции работы с удостоверениями
  • Функции установления соединения
  • Функции передачи данных
  • Служебные функции SSPI
  • Заглушки - неподдерживаемые функции SSPI
  • Примеры использования функций КриптоПро TLS


Функции работы с пакетами безопасности

Функции работы с пакетами безопасности используются при выборе и инициализации пакета.

Функции | Описание
EnumerateSecurityPackages | Функция EnumerateSecurityPackages() возвращает таблицу структур SecPkgInfo, состоящую из одного элемента, описывающего данный SSP.
InitSecurityInterface | Функция InitSecurityInterface() возвращает указатель на таблицу функций данного SSP. Это позволяет приложениям использовать SSPI без линковки с его конкретной реализацией.
QuerySecurityPackageInfo | Функция QuerySecurityPackageInfo() возвращает информацию о данном SSP.


EnumerateSecurityPackages

Функция EnumerateSecurityPackages() возвращает таблицу структур SecPkgInfo, состоящую из одного элемента, описывающего данный SSP.

SECURITY_STATUS EnumerateSecurityPackages(
  PULONG pcPackages,
  PSecPkgInfo * ppPackageInfo
);

Аргументы

pcPackages
[out] Указатель на ULONG, получает количество SSP (1 в случае данной библиотеки).
ppPackageInfo
[out] Указатель на переменную, которая получает адрес таблицы структур SecPkgInfo. Каждая структура описывает один SSP.

Возвращаемые значения:

Если функция завершается успешно, то возвращается SEC_E_OK. Иначе функция возвращает ненулевой код ошибки.

Примечания

Структура SecPkgInfo содержит информацию о SSP.

Для освобождения буфера, возвращенного в ppPackageInfo , надо вызвать функцию FreeContextBuffer.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

FreeContextBuffer() ,SecPkgInfo World Wide Web link


InitSecurityInterface

Функция InitSecurityInterface() возвращает указатель на таблицу функций данного SSP. Это позволяет приложениям использовать SSPI без линковки с его конкретной реализацией.

PSecurityFunctionTable InitSecurityInterface(
  void
);

Возвращаемые значения:

При успешном завершении функция возвращает указатель на структуру SecurityFunctionTable.

В противном случае функция возвращает NULL.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

SecurityFunctionTable World Wide Web link


QuerySecurityPackageInfo

Функция QuerySecurityPackageInfo() возвращает информацию о данном SSP.

SECURITY_STATUS QuerySecurityPackageInfo(
  SEC_CHAR * pszPackageName,
  PSecPkgInfo * ppPackageInfo
);

Аргументы

pszPackageName
[in] Имя SSP, о котором запрашивается информация.
ppPackageInfo
[out] Указатель на переменную, которая получает адрес структуры SecPkgInfo.

Возвращаемые значения:

Если функция завершается успешно, то возвращается SEC_E_OK. Иначе функция возвращает ненулевой код ошибки.

Примечания

Для освобождения буфера, возвращенного в ppPackageInfo , надо вызвать функцию FreeContextBuffer.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

FreeContextBuffer() ,SecPkgInfo World Wide Web link


Функции работы с удостоверениями

Функции работы с удостоверениями обеспечивают операции с удостоверениями субъектов безопасности.

Функции | Описание
AcquireCredentialsHandle | Функция AcquireCredentialsHandle() создает дескриптор удостоверения субъекта безопасности, требуемый для функций InitializeSecurityContext и AcceptSecurityContext.
FreeCredentialsHandle | Функция FreeCredentialsHandle() уведомляет систему о том, что удостоверение больше не используется. Когда все ссылки на него будут удалены, будет удалено и само удостоверение.
QueryCredentialsAttributes | Функция QueryCredentialsAttributes() возвращает информацию о свойствах данного удостоверения. Эта информация является общей для всех соединений, созданных с этим удостоверением.


AcquireCredentialsHandle

Функция AcquireCredentialsHandle() создает дескриптор удостоверения субъекта безопасности, требуемый для функций InitializeSecurityContext и AcceptSecurityContext.

SECURITY_STATUS AcquireCredentialsHandle(
  SEC_CHAR * pszPrincipal,
  SEC_CHAR * pszPackage,
  ULONG fCredentialUse,
  PLUID pvLogonID,
  PVOID pAuthData,
  PVOID pGetKeyFn,
  PVOID pvGetKeyArgument,
  PCredHandle phCredential,
  PTimeStamp ptsExpiry
);

Аргументы

pszPrincipal
[in] Параметр зарезервирован для будущего использования и должен быть NULL.
pszPackage
[in] Для SSL/TLS укажите UNISP_NAME (макрос, имеющий значение "Microsoft Unified Security Protocol Provider")
fCredentialUse
[in/out] Флаг возможного использования удостоверения:
Флаг | Смысл
SECPKG_CRED_INBOUND | Удостоверение сервера
SECPKG_CRED_OUTBOUND | Удостоверение клиента
pvLogonID
[in] Параметр зарезервирован для будущего использования и должен быть NULL.
pAuthData
[in] Структура SCHANNEL_CRED, содержащая все настройки, в том числе (опционально) сертификат субъекта.
pGetKeyFn
[in] Параметр зарезервирован для будущего использования и должен быть NULL.
pvGetKeyArgument
[in] Параметр зарезервирован для будущего использования и должен быть NULL.
phCredential
[out] Указатель на CredHandle. Сюда будет записан дескриптор удостоверения.
ptsExpiry
[out] Опционален. Указатель на TimeStamp. Параметр зарезервирован для будущего использования и должен быть NULL.

Возвращаемые значения:

Если функция завершается успешно, то возвращается SEC_E_OK. Иначе функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_UNKNOWN_CREDENTIALS | Параметр pAuthData содержал неподдерживаемое удостоверение.
SEC_E_NO_CREDENTIALS | Параметр fCredentialUse имеет неправильное значение.

Примечания

Когда удостоверение больше не используется, освободите его вызовом FreeCredentialsHandle.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

FreeCredentialsHandle() ,AcceptSecurityContext() ,InitializeSecurityContext()


FreeCredentialsHandle

Функция FreeCredentialsHandle() уведомляет систему о том, что удостоверение больше не используется. Когда все ссылки на него будут удалены, будет удалено и само удостоверение.

SECURITY_STATUS FreeCredentialsHandle(
  PCredHandle phCredential
);

Аргументы

phCredential
[in] Освобождаемый дескриптор.

Возвращаемые значения:

Если функция з0вершается успешно, то возвращается SEC_E_OK. Иначе функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_INVALID_HANDLE | Параметр phCredential не является дескриптором удостоверения.

Примечания

Эта функция позволяет системе освободить все ресурсы, связанные с удостоверением.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

AcquireCredentialsHandle()


QueryCredentialsAttributes

Функция QueryCredentialsAttributes() возвращает информацию о свойствах данного удостоверения. Эта информация является общей для всех соединений, созданных с этим удостоверением.

SECURITY_STATUS QueryCredentialsAttributes(
  PCredHandle phCredential,
  unsigned long ulAttribute,
  void SEC_FAR * pBuffer
);

Аргументы

phCredential
[in] Дескриптор интересующего удостоверения.
ulAttribute
[in] Указывает, какой атрибут запрашивается. Допустимые аттибуты перечислены в следующей таблице:
Значение | Возвращаемая информация | Тип структуры pBuffer
SECPKG_ATTR_SUPPORTED_ALGS | Поддерживаемые криптографические алгоритмы | SecPkgCred_SupportedAlgs
SECPKG_ATTR_CIPHER_STRENGTHS | Длина ключа в битах | SecPkgCred_CipherStrengths
SECPKG_ATTR_SUPPORTED_PROTOCOLS | Поддерживаемые протоколы | SecPkgCred_SupportedProtocols

pBuffer
[in/out] Указатель на структуру, куда будет помещен атрибут. Тип структуры зависит от параметра ulAttribute .

Возвращаемые значения:

При успешном завершении функция возвращает SEC_E_OK.

В случае ошибки функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_INVALID_HANDLE | Параметр phContext содержит недействительный дескриптор контекста.
SEC_E_UNSUPPORTED_FUNCTION | Параметр ulAttribute содержит неподдерживаемый атрибут.

Примечания

Изначально атрибуты соединения задаются в структуре SCHANNEL_CRED при вызове AcquireCredentialsHandle.

Память для структуры, указанной в pBuffer , отводит приложение, а память для динамических элементов этой структуры отводит SSP, и ее необходимо освободить с помощью FreeContextBuffer.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

AcquireCredentialsHandle() ,FreeContextBuffer() ,SCHANNEL_CRED World Wide Web link ,SecPkgCred_CipherStrengths World Wide Web link


Функции установления соединения

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

Функции | Описание
AcceptSecurityContext | Функция AcceptSecurityContext() создает серверный контекст безопасности, используемый для защищенного обмена между клиентом и сервером.
ApplyControlToken | Функция ApplyControlToken() используется для управления соединением. В настоящее время реализована только команда shutdown.
DeleteSecurityContext | Функция DeleteSecurityContext() освобождает структуры данных, связанные с данным соединением.
InitializeSecurityContext | Функция InitializeSecurityContext() создает клиентский контекст безопасности, используемый для защищенного обмена между клиентом и сервером.
QueryContextAttributes | Функция QueryContextAttributes() возвращает информацию о свойствах данного контекста.
QuerySecurityContextToken | Функция QuerySecurityContextToken() возвращает Windows NT/Windows 2000/Windows XP access token, соответсвующий удаленному пользователю.


AcceptSecurityContext

Функция AcceptSecurityContext() создает серверный контекст безопасности, используемый для защищенного обмена между клиентом и сервером.

Она обрабатывает входящие handshake-сообщения, и возвращает ответные handshake-сообщения, которые сервер должен передать клиенту.

SECURITY_STATUS AcceptSecurityContext(
  PCredHandle phCredential,
  PCtxtHandle phContext,
  PSecBufferDesc pInput,
  ULONG fContextReq,
  ULONG TargetDataRep,
  PCtxtHandle phNewContext,
  PSecBufferDesc pOutput,
  PULONG pfContextAttr,
  PTimeStamp ptsExpiry
);

Аргументы

phCredential
[in] Указатель на дескриптор удостоверения сервера. Указанный дескриптор получается через запрос к функции AcquireCredentialsHandle.
phContext
[in] Указатель на CtxtHandle World Wide Web link. При первом вызове, укажите NULL. При последующих - указатель на дескриптор, возвращенный в phNewContext после первого вызова к этой функции.
pInput
[in] указатель на структуру SecBufferDesc World Wide Web link, содержащую две структуры SecBuffer World Wide Web link. Первый буфер должен иметь тип SECBUFFER_TOKEN, и содержать пакет, полученый от клиента. Второй буфер должен иметь тип SECBUFFER_EMPTY
fContextReq
[in] Показывает, какой набор свойств должен поддерживать контекст. Допустимые флаги перечислены в следующей таблице:
Флаг | Описание
ASC_REQ_REPLAY_DETECT | Защита от навязывания повторных пакетов
ASC_REQ_SEQUENCE_DETECT | Защита от навязывания перестановок пакетов
ASC_REQ_CONFIDENTIALITY | Конциденциальность (шифрование)
ASC_REQ_STREAM | Соединение потокового типа
ASC_REQ_ALLOCATE_MEMORY | SSP отводит память для буферов. Ее необходимо освободить вызовом FreeContextBuffer
ASC_REQ_EXTENDED_ERROR | Уведомлять другую сторону об ошибках (SSL alerts)
ASC_REQ_CONNECTION | Неформатированные сообщения
ASC_REQ_MUTUAL_AUTH | Взаимная аутентификация
ASC_REQ_USE_SUPPLIED_CREDS | SSP не будет пытаться искать удостоверение, если оно не предоставлено приложением
ASC_REQ_MANUAL_CRED_VALIDATION | SSP не будет проверять удостоверение другой стороны
Может оказаться так, что клиент поддерживает не все из запрошенных параметров. Дальнейшие детали смотрите в описании параметра pfContextAttr
TargetDataRep
[in] Параметр зарезервирован для будущего использования и должен быть 0.
phNewContext
[in/out] Указатель на CtxtHandle World Wide Web link. При первом вызове сюда будет помещен дескриптор нового контекста. При последующих вызовах полученный дескриптор следует передавать через параметр phContext , а в phNewContext передавать NULL
pOutput
[in] Указатель на структуру SecBufferDesc World Wide Web link, содержащую SecBuffer World Wide Web link с типом SECBUFFER_TOKEN. На выходе в этот буфер будет помещен пакет, который нужно переслать клиенту. Функция может дополнительно вернуть буфер с типом SECBUFFER_EXTRA.
pfContextAttr
[out] Указатель на ULONG, куда будут записаны флаги, описывающие свойства устанавливаемого контекста. Список возможных значений приведен в описании параметра fContextReq . pfContextAttr получает тот же набор флагов, с заменой префикса ASC_REQ на ASC_RET.
Warning Атрибуты безопасности не следует проверять до последнего вызова функции, возвращающего SEC_E_OK. Остальные атрибуты, такие как ASC_RET_ALLOCATED_MEMORY можно использовать после первого же вызова.
ptsExpiry
[out] Опционален. Указатель на структуру TimeStamp. Gри наличии сертификата клиента, этот параметр получает время истечения этого сертификата. Иначе возвращается максимальное возможное значение времени.

Возвращаемые значения:

При успешном завершении функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_OK | Контекст безопасности был успешно установлен. Дальнейших вызовов к этой функции не требуется. Если функция вернула данные в pOutput, их следует переслать клиенту.
SEC_E_INCOMPLETE_MESSAGE | Данные во входном буфере неполны. Приложение должно прочитать остальные данные, и снова вызвать AcceptSecurityContext .
SEC_I_CONTINUE_NEEDED | Сервер должен послать выходные данные клиенту и ждать ответа от клиента. Полученный ответ полжен быть передан при следующем вызове в AcceptSecurityContext через параметр pInput .

Возвращаемые значения:

В случае ошибки функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_INVALID_TOKEN | Параметр pInput содержит неправильные входные данные.
SEC_E_INVALID_HANDLE | Параметр phContext содержит недействительный дескриптор контекста.
SEC_E_UNSUPPORTED_FUNCTION | Параметр fContextReq содержит неподдерживаемый набор флагов.
SEC_E_INSUFFICIENT_MEMORY | Один из буферов оказался слишком мал. См. заметки.

Примечания

На стороне клиента для создания контекста используется парная функция InitializeSecurityContext.
При получении кода ошибки SEC_E_INSUFFICIENT_MEMORY, укажите атрибут ASC_REQ_ALLOCATE_MEMORY в fContextReq .
Вызывающее приложение должно определить допустимость атрибутов установленного контекста. Например, если конфиденциальность соединения была запрошена, но не была достигнута, некоторые приложения должны принять решение немедленно прекратить соединение. Если контекст не смог быть установлен, сервер должен удалить частично созданный контекст вызовом DeleteSecurityContext.
После установления контекста, серверное приложение может с помощью функции QuerySecurityContextToken получить дескриптор пользователя, которому сопоставлен сертификат клиента, и использовать его для имперсонализации пользователя.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

AcquireCredentialsHandle() ,CtxtHandle World Wide Web link ,DeleteSecurityContext() ,InitializeSecurityContext() ,SecBuffer World Wide Web link ,SecBufferDesc World Wide Web link ,FreeContextBuffer() ,TimeStamp World Wide Web link


ApplyControlToken

Функция ApplyControlToken() используется для управления соединением. В настоящее время реализована только команда shutdown.

SECURITY_STATUS ApplyControlToken(
  PCtxtHandle phContext,
  PSecBufferDesc pInput
);

Аргументы

phContext
[in] Дескриптор контекста.
pInput
[in] Структура SecBufferDesc, состоящая из единственной структуры SecBuffer. Буфер должен иметь тип SECBUFFER_TOKEN, длину sizeof(DWORD), и значение - DWORD, содержащий управляющую команду. Допустимые аттибуты перечислены в следующей таблице:
Значение | Операция
SCHANNEL_SHUTDOWN | Нормальное завершение соединения с уведомлением другой стороны.

Возвращаемые значения:

При успешном завершении функция возвращает SEC_E_OK.
В противном случае функция возвращает ненулевой код ошибки.

Примечания

За описанием того, как SSP уведомляет приложение на другой стороне о закрытии соединения командой SCHANNEL_SHUTDOWN см. заметки к DecryptMessage.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

DecryptMessage() ,SecBuffer World Wide Web link ,SecBufferDesc World Wide Web link


DeleteSecurityContext

Функция DeleteSecurityContext() освобождает структуры данных, связанные с данным соединением.

SECURITY_STATUS DeleteSecurityContext(
  PCtxtHandle phContext
);

Аргументы

phContext
[in] Дескриптор удаляемого контекста соединения.

Возвращаемые значения:

Если функция завершается успешно, то возвращается SEC_E_OK. Иначе функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_INVALID_HANDLE | Параметр phContext содержит недействительный дескриптор контекста.

Примечания

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

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.


InitializeSecurityContext

Функция InitializeSecurityContext() создает клиентский контекст безопасности, используемый для защищенного обмена между клиентом и сервером.

Возвращает handshake-сообщения, которые клиент должен передать серверу, и обрабатывает ответные handshake-сообщения.

SECURITY_STATUS InitializeSecurityContext(
  PCredHandle phCredential,
  PCtxtHandle phContext,
  SEC_CHAR * pszTargetName,
  ULONG fContextReq,
  ULONG Reserved1,
  ULONG TargetDataRep,
  PSecBufferDesc pInput,
  ULONG Reserved2,
  PCtxtHandle phNewContext,
  PSecBufferDesc pOutput,
  PULONG pfContextAttr,
  PTimeStamp ptsExpiry
);

Аргументы

phCredential
[in] Указатель на дескриптор удостоверения клиента. Указанный дескриптор получается через запрос к функции AcquireCredentialsHandle.
phContext
[in] Указатель на CtxtHandle World Wide Web link. При первом вызове, укажите NULL. При последующих - указатель на дескриптор, возвращенный в phNewContext после первого вызова к этой функции.
pszTargetName
[in] Строка, являющаяся уникальным идентификатором сервера. Используется при поиске сессии в кеше сессий при восстановлении соединения.
fContextReq
[in] Показывает, какой набор свойств должен поддерживать контекст. Допустимые флаги перечислены в следующей таблице:
Флаг | Описание
ISC_REQ_REPLAY_DETECT | Защита от навязывания повторных пакетов
ISC_REQ_SEQUENCE_DETECT | Защита от навязывания перестановок пакетов
ISC_REQ_CONFIDENTIALITY | Конциденциальность (шифрование)
ISC_REQ_STREAM | Соединение потокового типа
ISC_REQ_ALLOCATE_MEMORY | SSP отводит память для буферов. Ее необходимо освободить вызовом FreeContextBuffer
ISC_REQ_EXTENDED_ERROR | Уведомлять другую сторону об ошибках (SSL alerts)
ISC_REQ_CONNECTION | Неформатированные сообщения
ISC_REQ_MUTUAL_AUTH | Взаимная аутентификация
ISC_REQ_USE_SUPPLIED_CREDS | SSP не будет пытаться искать удостоверение, если оно не предоставлено приложением
ISC_REQ_MANUAL_CRED_VALIDATION | SSP не будет проверять удостоверение другой стороны
Может оказаться так, что клиент поддерживает не все из запрошенных параметров. Дальнейшие детали смотрите в описании параметра pfContextAttr
Reserved1
[in] Зарезервирован. Должен быть 0.
TargetDataRep
[in] Параметр зарезервирован для будущего использования и должен быть 0.
pInput
[in] указатель на структуру SecBufferDesc World Wide Web link. При первом вызове должен быть NULL. При последующих вызовах должен состоять из двух структур SecBuffer World Wide Web link. Первый буфер должен иметь тип SECBUFFER_TOKEN, и содержать пакет, полученый от сервера. Второй буфер должен иметь тип SECBUFFER_EMPTY. В него в случае необходимости будут помещены оставшиеся необработанные входные данные.
Reserved2
[in] Зарезервирован. Должен быть 0.
phNewContext
[in/out] Указатель на CtxtHandle World Wide Web link. При первом вызове сюда будет помещен дескриптор нового контекста. При последующих вызовах полученный дескриптор следует передавать через параметр phContext , а в phNewContext передавать NULL
pOutput
[in] Указатель на структуру SecBufferDesc World Wide Web link, содержащую SecBuffer World Wide Web link с типом SECBUFFER_TOKEN. На выходе в этот буфер будет помещен пакет, который нужно переслать серверу. При наличии флага ISC_REQ_ALLOCATE_MEMORY этот буфер будет отведен.
pfContextAttr
[out] Указатель на ULONG, куда будут записаны флаги, описывающие свойства устанавливаемого контекста. Список возможных значений приведен в описании параметра fContextReq . pfContextAttr получает тот же набор флагов, с заменой префикса ISC_REQ на ISC_RET.
Warning Аттрибуты безопасности не следует проверять до последнего вызова функции, возвращающего SEC_E_OK. Остальные атрибуты, такие как ISC_RET_ALLOCATED_MEMORY можно использовать после первого же вызова.
ptsExpiry
[out] Опционален. Указатель на структуру TimeStamp. Gри наличии сертификата клиента, этот параметр получает время истечения этого сертификата. Иначе возвращается максимальное возможное значение времени.

Возвращаемые значения:

При успешном завершении функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_OK | Контекст безопасности был успешно установлен. Дальнейших вызовов к этой функции не требуется. Если функция вернула данные в pOutput, их следует переслать серверу.
SEC_E_INCOMPLETE_MESSAGE | Данные во входном буфере неполны. Приложение должно прочитать остальные данные, и снова вызвать InitializeSecurityContext .
SEC_I_INCOMPLETE_CREDENTIALS | Сервер запросил аутентификацию клиента, а указанное удостоверение не содержит сертификата, или сертификат издан не доверенным центром сертификации.
SEC_I_CONTINUE_NEEDED | Клиент должен послать выходные данные серверу и ждать ответа. Полученный ответ полжен быть передан при следующем вызове в InitializeSecurityContext через параметр pInput .

Возвращаемые значения:

В случае ошибки функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_INVALID_TOKEN | Параметр pInput содержит неправильные входные данные.
SEC_E_INVALID_HANDLE | Параметр phContext содержит недействительный дескриптор контекста.
SEC_E_NO_CREDENTIALS | Предоставленное удостоверение недостоверно.
SEC_E_UNSUPPORTED_FUNCTION | Параметр fContextReq содержит неподдерживаемый набор флагов.
SEC_E_INSUFFICIENT_MEMORY | Один из буферов оказался слишком мал. См. заметки.

Примечания

На стороне сервера для создания контекста используется парная функция AcceptSecurityContext. При получении кода ошибки SEC_E_INSUFFICIENT_MEMORY, укажите атрибут ISC_REQ_ALLOCATE_MEMORY в fContextReq . При получении кода SEC_I_INCOMPLETE_CREDENTIALS, убедитесь, что при вызове AcquireCredentialsHandle был указан действующий сертификат, изданный центром, которому доверяет сервер. Список таких центров может быть получен вызовом QueryContextAttributes с атрибутом SECPKG_ATTR_ISSUER_LIST_EX. Найдя такой сертификат, создайте новое удостоверение вызовом к AcquireCredentialsHandle, и вызовите InitializeSecurityContext еще раз, с новым удостоверением.
Вызывающее приложение должно определить допустимость атрибутов установленного контекста. Например, если конфиденциальность соединения была запрошена, но не была достигнута, некоторые приложения должны принять решение немедленно прекратить соединение. Если контекст не смог быть установлен, сервер должен удалить частично созданный контекст вызовом DeleteSecurityContext.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

AcquireCredentialsHandle() ,CtxtHandle World Wide Web link ,DeleteSecurityContext() ,AcceptSecurityContext() ,SecBuffer World Wide Web link ,SecBufferDesc World Wide Web link ,FreeContextBuffer() ,TimeStamp World Wide Web link


QueryContextAttributes

Функция QueryContextAttributes() возвращает информацию о свойствах данного контекста.

SECURITY_STATUS QueryContextAttributes(
  PCtxtHandle phContext,
  ULONG ulAttribute,
  PVOID pBuffer
);

Аргументы

phContext
[in] Дескриптор интересующего контекста.
ulAttribute
[in] Указывает, какой атрибут запрашивается. Допустимые аттибуты перечислены в следующей таблице:
Значение | Возвращаемая информация | Тип структуры pBuffer
SECPKG_ATTR_CONNECTION_INFO | Протокол и используемые крипт. алгоритмы. | SecPkgContext_ConnectionInfo
SECPKG_ATTR_ISSUER_LIST_EX | Список доверенных центров сертификации. | SecPkgContext_IssuerListInfoEx
SECPKG_ATTR_LIFESPAN | Время истечения соединения. | SecPkgContext_Lifespan
SECPKG_ATTR_NAMES | Subject из сертификата удаленной стороны. | SecPkgContext_Names
SECPKG_ATTR_REMOTE_CERT_CONTEXT | Сертификат удаленной стороны. | CERT_CONTEXT . См. заметки.
SECPKG_ATTR_STREAM_SIZES | Размеры буферов для заголовка, блока данных и трейлера при шифровании. | SecPkgContext_StreamSizes

pBuffer
[in/out] Указатель на структуру, куда будет помещен атрибут. Тип структуры зависит от параметра ulAttribute . См. Заметки.

Возвращаемые значения:

При успешном завершении функция возвращает SEC_E_OK.

В противном случае функция возвращает ненулевой код ошибки.

Примечания

Память для структуры, указанной в pBuffer , отводит приложение, а память для динамических элементов этой структуры отводит SSP, и ее необходимо освободить с помощью FreeContextBuffer.

Аттрибут SECPKG_ATTR_REMOTE_CERT_CONTEXT возвращает структуру CERT_CONTEXT. Ее необходимо освободить с помощью CertFreeCertificateContext.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.


QuerySecurityContextToken

Функция QuerySecurityContextToken() возвращает Windows NT/Windows 2000/Windows XP access token, соответсвующий удаленному пользователю.

SECURITY_STATUS QuerySecurityContextToken(
  PCtxtHandle phContext,
  HANDLE * phToken
);

Аргументы

phContext
[in] Дескриптор интересующего контекста.
phToken
[out] Указатель, куда помещается token.

Возвращаемые значения:

При успешном завершении функция возвращает SEC_E_OK.

Иначе функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_INVALID_HANDLE | Параметр phContext содержит недействительный дескриптор контекста.

Примечания

Эта функция вызывается приложением для имперсонализации пользователя. По окончании использования возвращенный дескриптор должен быть освобожден вызовом CloseHandle.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.


Функции передачи данных

Функции передачи данных предназначены для выполнения операций шифрования и расшифрования потока данных, а также контроля его целостности.

Функции | Описание
DecryptMessage | Функция DecryptMessage() расшифровывает сообщение, проверяет его целостность и уведомляет о получении запроса на переустановку связи или разрыв соединения.
EncryptMessage | Функция EncryptMessage() зашифровывает сообщение с использованием сессионного ключа, выработанного совместно с удаленной стороной, и вычисляет имитовставку.


DecryptMessage

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

SECURITY_STATUS DecryptMessage(
  PCtxtHandle phContext,
  PSecBufferDesc pMessage,
  ULONG MessageSeqNo,
  PULONG pfQOP
);

Примечания

Функция не может быть вызвана асинхронно (одновременно из разных нитей) над одним и тем же контекстом. Последовательность пакетов существенна для TLS.

Аргументы

phContext
[in] Дескриптор контекста соединения, установленного с отправителем сообщения.
pMessage
[in/out] Указатель на структуру SecBufferDesc, содержащую 4 буфера SecBuffer. Ровно один из них должен иметь тип SECBUFFER_DATA, и содержать зашифрованные данные, которые будут расшифрованы на месте. Остальные буферы должны иметь тип SECBUFFER_EMPTY.
MessageSeqNo
[in] Параметр зарезервирован для будущего использования и должен быть 0.
pfQOP
[in] Параметр зарезервирован для будущего использования и должен быть NULL.

Возвращаемые значения:

Если функция завершается успешно, и другая сторона не запросила изменения состояния соединения (renegotiation или shutdown), то возвращается SEC_E_OK. Иначе функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_INVALID_HANDLE | Параметр phContext содержит недействительный дескриптор контекста.
SEC_E_INVALID_TOKEN | Параметр pInput содержит неправильные входные данные.
SEC_E_INCOMPLETE_MESSAGE | Данные во входном буфере неполны. Приложение должно прочитать остальные данные, и повторить вызов.
SEC_I_RENEGOTIATE | Другая сторона запросила renegotiate. См. заметки.
SEC_I_CONTEXT_EXPIRED | Другая сторона закрыла соединение. См. заметки.

Примечания

Функция возвращает SEC_I_CONTEXT_EXPIRED, когда отправитель завершил соединение. После получения этого сообщения нужно:

  • Вызвать EncryptMessage с пустым входным буфером.
  • Отправить полученный вывод.
  • Удалить контекст вызовом DeleteSecurityContext

    • Код возврата SEC_I_RENEGOTIATE означает, что отправитель хочет переустановить соединение. В этом случае необходимо вызвать AcceptSecurityContext (для сервера) или InitializeSecurityContext (для клиента), передав пустой входной буфер. После этого первого вызова, повторяется та же цепочка действий, что и при первоначальном создании контекста.

    Требования:

    Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
    Solaris: 9 Update 4 или выше.
    FreeBSD: FreeBSD 5.2 или выше
    Linux: RedHat 7.3, RedHad 9.0.
    Файл описания: Прототип описан в файле sspi.h.

    См. также:

    AcceptSecurityContext() ,InitializeSecurityContext() ,DeleteSecurityContext() ,EncryptMessage() ,SecBuffer World Wide Web link ,SecBufferDesc World Wide Web link


EncryptMessage

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

SECURITY_STATUS EncryptMessage(
  PCtxtHandle phContext,
  ULONG fQOP,
  PSecBufferDesc pMessage,
  ULONG MessageSeqNo
);

Примечания

Функция не может быть вызвана асинхронно (одновременно из разных нитей) над одним и тем же контекстом. Последовательность пакетов существенна для TLS.

Аргументы

phContext
[in] Дескриптор контекста соединения, установленного с получателем сообщения.
fQOP
[in] Параметр зарезервирован для будущего использования и должен быть 0.
pMessage
[in/out] Указатель на структуру SecBufferDesc, содержащую 4 буфера SecBuffer.
Тип | Заполнение
SECBUFFER_STREAM_HEADER | Можно не инициализировать
SECBUFFER_DATA | Должен содержать данные, которые будут зашифрованы, и их длину
SECBUFFER_STREAM_TRAILER | Можно не инициализировать
SECBUFFER_EMPTY | Пустой
Размеры буферов могут быть получены вызовом QueryContextAttributes с параметром SECPKG_ATTR_STREAM_SIZES. Она возвращает структуру SecPkgContext_StreamSizes, поля которой cbHeader, cbMaximumMessage и cbTrailer содержат размеры соответственно первого, второго и третьего буферов.

Память для этих буферов должна идти подряд одним куском.
MessageSeqNo
[in] Параметр зарезервирован для будущего использования и должен быть 0.

Возвращаемые значения:

Если функция завершается успешно, то возвращается SEC_E_OK. Иначе функция возвращает одно из следующих значений:
Коды возврата | Описание
SEC_E_INVALID_HANDLE | Параметр phContext содержит недействительный дескриптор контекста.
SEC_E_INVALID_TOKEN | Параметр pInput содержит неправильные входные данные.
SEC_E_CONTEXT_EXPIRED | Другая сторона закрыла соединение. См. заметки.

Примечания

Приложение должно отправить получателю все три полученные буфера - header, data и trailer.

Функция возвращает SEC_E_CONTEXT_EXPIRED, если получатель завершил соединение. После получения этого сообщения нужно:

  • Отправить полученный вывод.
  • Удалить контекст вызовом DeleteSecurityContext

    • Требования:

    Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
    Solaris: 9 Update 4 или выше.
    FreeBSD: FreeBSD 5.2 или выше
    Linux: RedHat 7.3, RedHad 9.0.
    Файл описания: Прототип описан в файле sspi.h.

    См. также:

    QueryContextAttributes() ,SecPkgContext_StreamSizes World Wide Web link ,SecBuffer World Wide Web link ,SecBufferDesc World Wide Web link


Служебные функции SSPI

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

Функции | Описание
FreeContextBuffer | Функция FreeContextBuffer() освобождает память, отведенную SSP, например, буфер, отведенный функцией InitializeSecurityContext для выходных данных.


FreeContextBuffer

Функция FreeContextBuffer() освобождает память, отведенную SSP, например, буфер, отведенный функцией InitializeSecurityContext для выходных данных.

SECURITY_STATUS FreeContextBuffer(
  PVOID pvContextBuffer
);

Возвращаемые значения:

Если функция завершается успешно, то возвращается SEC_E_OK. Иначе функция возвращает ненулевой код ошибки.

Примечания

Функция может освободить любую память, отведенную SSP.

Требования:

Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
Solaris: 9 Update 4 или выше.
FreeBSD: FreeBSD 5.2 или выше
Linux: RedHat 7.3, RedHad 9.0.
Файл описания: Прототип описан в файле sspi.h.

См. также:

InitializeSecurityContext()


Заглушки - неподдерживаемые функции SSPI

При обращении к этим функциям всегда возвращается код ошибки.

Функции | Описание
CompleteAuthToken | Функция CPCompleteAuthToken() завершает аутентификацию пакета. Используется, в частности, протоколом DCE.
ExportSecurityContext | Функция CPExportSecurityContext() сохраняет контекст в буфере.
ImpersonateSecurityContext | Функция CPImpersonateSecurityContext() позволяет серверу выступать от лица клиента при всех проверках прав доступа.
ImportSecurityContext | Функция CPImportSecurityContextA() считывает контекст из буфера.
MakeSignature | Функция CPMakeSignature() функция подписи сообщения. Эта функция интерфейса SSPI не поддерживается данным SSP, так как используются функция EncryptMessage, осуществляющая шифрование и подпись сообщения.
RevertSecurityContext | Функция CPRevertSecurityContext() запрещает серверу выступать от лица клиента при всех проверках прав доступa.
VerifySignature | Функция VerifySignature() осуществляет проверку подписи сообщения. Эта функция интерфейса SSPI не поддерживается данным SSP, так как используется функция DecryptMessage, осуществляющая расшифрование и проверку подписи сообщения.


CompleteAuthToken

Функция CPCompleteAuthToken() завершает аутентификацию пакета. Используется, в частности, протоколом DCE.

SECURITY_STATUS CompleteAuthToken(
  PCtxtHandle phContext,
  PSecBufferDesc pToken
);

Возвращаемые значения:

Всегда возвращает SEC_E_INTERNAL_ERROR.
Крипто-Про SSPI Версия: 3.0 Сборка 3293
Что Вы думаете по поводу данной статьи?
 Закажите CD c Крипто-Про CSP


ExportSecurityContext

Функция CPExportSecurityContext() сохраняет контекст в буфере.

SECURITY_STATUS ExportSecurityContext(
  PCtxtHandle phContext,
  ULONG fFlags,
  PSecBuffer pPackedContext,
  void SEC_FAR *SEC_FAR * pToken
);

Возвращаемые значения:

Всегда возвращает SEC_E_INTERNAL_ERROR.
Крипто-Про SSPI Версия: 3.0 Сборка 3293
Что Вы думаете по поводу данной статьи?
 Закажите CD c Крипто-Про CSP


ImpersonateSecurityContext

Функция CPImpersonateSecurityContext() позволяет серверу выступать от лица клиента при всех проверках прав доступа.

SECURITY_STATUS ImpersonateSecurityContext(
  PCtxtHandle phContext
);

Возвращаемые значения:

Всегда возвращает SEC_E_INTERNAL_ERROR.
Крипто-Про SSPI Версия: 3.0 Сборка 3293
Что Вы думаете по поводу данной статьи?
 Закажите CD c Крипто-Про CSP


ImportSecurityContext

Функция CPImportSecurityContextA() считывает контекст из буфера.

SECURITY_STATUS ImportSecurityContext(
  SEC_CHAR SEC_FAR * pszPackage,
  PSecBuffer pPackedContext,
  void SEC_FAR * pToken,
  PCtxtHandle phContext
);

Возвращаемые значения:

Всегда возвращает SEC_E_INTERNAL_ERROR.
Крипто-Про SSPI Версия: 3.0 Сборка 3293
Что Вы думаете по поводу данной статьи?
 Закажите CD c Крипто-Про CSP


MakeSignature

Функция CPMakeSignature() функция подписи сообщения. Эта функция интерфейса SSPI не поддерживается данным SSP, так как используются функция EncryptMessage, осуществляющая шифрование и подпись сообщения.

SECURITY_STATUS MakeSignature(
  PCtxtHandle phContext,
  ULONG fQOP,
  PSecBufferDesc pMessage,
  ULONG MessageSeqNo
);

Возвращаемые значения:

Всегда возвращает SEC_E_INTERNAL_ERROR.
Крипто-Про SSPI Версия: 3.0 Сборка 3293
Что Вы думаете по поводу данной статьи?
 Закажите CD c Крипто-Про CSP


RevertSecurityContext

Функция CPRevertSecurityContext() запрещает серверу выступать от лица клиента при всех проверках прав доступa.

SECURITY_STATUS RevertSecurityContext(
  PCtxtHandle phContext
);

Возвращаемые значения:

Всегда возвращает SEC_E_INTERNAL_ERROR.
Крипто-Про SSPI Версия: 3.0 Сборка 3293
Что Вы думаете по поводу данной статьи?
 Закажите CD c Крипто-Про CSP


VerifySignature

Функция VerifySignature() осуществляет проверку подписи сообщения. Эта функция интерфейса SSPI не поддерживается данным SSP, так как используется функция DecryptMessage, осуществляющая расшифрование и проверку подписи сообщения.

SECURITY_STATUS VerifySignature(
  PCtxtHandle phContext,
  PSecBufferDesc pMessage,
  ULONG MessageSeqNo,
  PULONG pfQOP
);

Возвращаемые значения:

Всегда возвращает SEC_E_INTERNAL_ERROR.
Крипто-Про SSPI Версия: 3.0 Сборка 3293
Что Вы думаете по поводу данной статьи?
 Закажите CD c Крипто-Про CSP


Примеры использования функций КриптоПро TLS

Приведенные ниже примеры демонстрируют использование основных функций СКЗИ КриптоПро TLS.

  • Примеры взаимодействия web клиента и web сервера.


Примеры взаимодействия web клиента и web сервера.

В данном разделе приводятся примеры, демонстрирующие взаимодействие web клиента и web сервера при помощи интерфейса SSPI.

  • Пример создания соединения сервера с web клиентом
  • Пример создания соединения клиента с web сервером


Крипто-Про SSPI

Данный пример демонстрирует установление соединения сервера с web клиентом по защищенному каналу.

Этот пример демонстрирует использование следующих функций

  • QueryContextAttributes
  • EncryptMessage
  • DecryptMessage
  • AcquireCredentialsHandle
  • FreeCredentialsHandle
  • AcceptSecurityContext
  • DeleteSecurityContext
  • FreeContextBuffer
  • ApplyControlToken

//--------------------------------------------------------------------
// В данном примере устанавливается соединение сервера с web клиентом
// по защищенному каналу.
// Используемый сертификат должен создаваться как
// Server Authentication Certificate.
//--------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <schannel.h>

#define SECURITY_WIN32
#include <security.h>
#include <sspi.h>

#define IO_BUFFER_SIZE  0x10000
#define NT4_DLL_NAME TEXT("Security.dll")

// Опции пользователя.
INT     iPortNumber     = 443;
LPSTR   pszUserName     = NULL;
DWORD   dwProtocol      = 0;

HCERTSTORE  hMyCertStore = NULL;

CHAR IoBuffer[IO_BUFFER_SIZE];
DWORD cbIoBuffer = 0;

HMODULE g_hSecurity = NULL;

PSecurityFunctionTable g_pSSPI;

static
HRESULT
CreateCredentials(
    LPSTR pszUserName,
    PCredHandle phCreds);

static void
WebServer(CredHandle *phServerCreds);

static
BOOL
ParseRequest (
    IN PCHAR InputBuffer,
    IN INT InputBufferLength,
    OUT PCHAR ObjectName,
    OUT DWORD *pcbContentLength);

static
BOOL
SSPINegotiateLoop(
    SOCKET          Socket,
    PCtxtHandle     phContext,
    PCredHandle     phCred,
    BOOL            fDoInitialRead,
    BOOL            NewContext);

static
LONG
DisconnectFromClient(
    SOCKET          Socket,
    PCredHandle     phCreds,
    CtxtHandle *    phContext);

static void PrintHexDump(DWORD length, PCHAR buffer);

//-------------------------------------------------------------
// Функция вывода информации об использовании данного примера.

void Usage(void)
{
    printf("\n");
    printf("USAGE: webserver -u<user> \n");
    printf("\n");
    printf("    -u<user>        Name of user (in existing certificate)\n");

    exit(1);
} //Usage

//-------------------------------------------------------------
// Функция загрузки библиотеки.

BOOL
LoadSecurityLibrary(void)
{
    INIT_SECURITY_INTERFACE         pInitSecurityInterface;
    CHAR lpszDLL[MAX_PATH];

    //
    //  Определение используемой библиотеки
    //

        strcpy (lpszDLL, NT4_DLL_NAME );

    //
    //  Загрузка библиотеки
    //

    g_hSecurity = LoadLibrary(lpszDLL);
    if(g_hSecurity == NULL)
    {
        printf("Error 0x%x loading %s.\n", GetLastError(), lpszDLL);
        return FALSE;
    }

    pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(
                                    g_hSecurity,
                                    "InitSecurityInterfaceA");

    if(pInitSecurityInterface == NULL)
    {
        printf("Error 0x%x reading InitSecurityInterface entry point.\n",
               GetLastError());
        return FALSE;
    }

    g_pSSPI = pInitSecurityInterface();

    if(g_pSSPI == NULL)
    {
        printf("Error 0x%x reading security interface.\n",
               GetLastError());
        return FALSE;
    }

    return TRUE;
} //LoadSecurityLibrary

//-------------------------------------------------------------
// Функция main.

void _cdecl main(int argc, char *argv[])
{
    WSADATA WsaData;
    CredHandle hServerCreds;

    INT i;
    INT iOption;
    PCHAR pszOption;

    //
    // Разбор командной строки.
    //

    if(argc <= 1)
    {
        Usage();
    }

    for(i = 1; i < argc; i++)
    {
        if(argv[i][0] == '/') argv[i][0] = '-';

        if(argv[i][0] != '-')
        {
            printf("**** Invalid argument \"%s\"\n", argv[i]);
            Usage();
        }

        iOption = argv[i][1];
        pszOption = &argv[i][2];

        switch(iOption)
        {

        case 'u':
            pszUserName = pszOption;
            break;

        default:
            printf("**** Invalid option \"%s\"\n", argv[i]);
            Usage();
        }
    }

    if(!LoadSecurityLibrary())
    {
        printf("Error initializing the security library\n");
        return;
    }

    //
    // Инициализация подсистемы WinSock.
    //

    if(WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
    {
        printf("Error %d returned by WSAStartup\n", GetLastError());
        exit(1);
    }

    //
    // Создание мандатов сервера.
    //

    if(CreateCredentials(pszUserName, &hServerCreds))
    {
        printf("Error creating credentials\n");
        exit(1);
    }

    WebServer(&hServerCreds);

    // Освобождение дескриптора SSPI мандатов.
    g_pSSPI->FreeCredentialsHandle(&hServerCreds);

    // Завершение работы подсистемы WinSock.
    WSACleanup();

    // Закрытие хранилища сертификатов "MY".
    if(hMyCertStore)
    {
        CertCloseStore(hMyCertStore, 0);
    }

    exit(0);
} //main

//-------------------------------------------------------------
// Функция создания соединения с web сервером.

static void
WebServer(CredHandle *phServerCreds)
{
    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET Socket = INVALID_SOCKET;
    SOCKADDR_IN address;
    SOCKADDR_IN remoteAddress;
    INT remoteSockaddrLength;
    DWORD cConnections = 0;
    INT err;
    INT i;

    CHAR objectName[256];
    DWORD currentDirectoryLength;
    HANDLE objectHandle = INVALID_HANDLE_VALUE;
    BY_HANDLE_FILE_INFORMATION fileInfo;
    INT bytesSent;
    DWORD bytesRead;
    DWORD cbContentLength;

    CtxtHandle      hContext;
    BOOL            fContextInitialized = FALSE;
    SecBufferDesc   Message;
    SecBuffer       Buffers[4];
    SecBufferDesc   MessageOut;
    SecBuffer       OutBuffers[4];
    SecPkgContext_StreamSizes Sizes;
    SECURITY_STATUS scRet;

    HANDLE hUserToken = NULL;
    BOOL fImpersonating = FALSE;

    //
    // Инициализация структур закрытых буферов
    //

    Message.ulVersion = SECBUFFER_VERSION;
    Message.cBuffers = 4;
    Message.pBuffers = Buffers;

    Buffers[0].BufferType = SECBUFFER_EMPTY;
    Buffers[1].BufferType = SECBUFFER_EMPTY;
    Buffers[2].BufferType = SECBUFFER_EMPTY;
    Buffers[3].BufferType = SECBUFFER_EMPTY;

    MessageOut.ulVersion = SECBUFFER_VERSION;
    MessageOut.cBuffers = 4;
    MessageOut.pBuffers = OutBuffers;

    OutBuffers[0].BufferType = SECBUFFER_EMPTY;
    OutBuffers[1].BufferType = SECBUFFER_EMPTY;
    OutBuffers[2].BufferType = SECBUFFER_EMPTY;
    OutBuffers[3].BufferType = SECBUFFER_EMPTY;

    //
    // Вычисление текущей директории для получения необходимых
    // файлов из нее.
    //

    currentDirectoryLength = GetCurrentDirectory( 256, objectName );
    if ( currentDirectoryLength == 0 )
    {
        printf( "GetCurrentDirectory failed: %ld\n", GetLastError( ) );
        exit(1);
    }

    //
    // Настройка сокета, слушающего порт HTTPS.
    //

    ListenSocket = socket( AF_INET, SOCK_STREAM, 0 );
    if ( ListenSocket == INVALID_SOCKET )
    {
        printf( "socket() failed for ListenSocket: %ld\n", GetLastError( ) );
        exit(1);
    }

    RtlZeroMemory( &address, sizeof(address) );
    address.sin_family = AF_INET;
    address.sin_port = htons( (short)iPortNumber );    // порт https
    address.sin_addr.s_addr = 0;

    err = bind(ListenSocket, (PSOCKADDR) &address, sizeof(address));
    if (err == SOCKET_ERROR)
    {
        printf("bind failed: %ld\n", GetLastError());
        exit(1);
    }

    err = listen(ListenSocket, 1);
    if (err == SOCKET_ERROR)
    {
        printf("listen failed: %ld\n", GetLastError());
        exit(1);
    }

    //
    // Цикл по соединениям.
    //

    for (;;)
    {
        PSecBuffer pDataBuffer;

        fContextInitialized = FALSE;

        objectHandle = INVALID_HANDLE_VALUE;

        //
        // В первую очередь получение соединения.
        //

        printf("\nWaiting for connection %d\n", ++cConnections);

        remoteSockaddrLength = sizeof(remoteAddress);

        Socket = accept(ListenSocket,
                        (LPSOCKADDR)&remoteAddress,
                        &remoteSockaddrLength);
        if(Socket == INVALID_SOCKET)
        {
            printf( "accept() failed: %ld\n", GetLastError( ) );
            goto cleanup;
        }

        printf("Socket connection established\n");

        //
        // Установление связи
        //

        cbIoBuffer = 0;

        if(!SSPINegotiateLoop(Socket,
                          &hContext,
                          phServerCreds,
                          TRUE,
                          TRUE))
        {
            printf("Couldn't connect\n");
            goto cleanup;
        }

        fContextInitialized = TRUE;

        //
        // Вычисление размера заголовка:
        //

        scRet = g_pSSPI->QueryContextAttributes(&hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);

        if(scRet != SEC_E_OK)
        {
            printf("Couldn't get Sizes\n");
            goto cleanup;
        }

        //
        // Получение HTTP запроса от клиента.
        //

        do
        {
            Buffers[0].pvBuffer = IoBuffer;
            Buffers[0].cbBuffer = cbIoBuffer;
            Buffers[0].BufferType = SECBUFFER_DATA;

            Buffers[1].BufferType = SECBUFFER_EMPTY;
            Buffers[2].BufferType = SECBUFFER_EMPTY;
            Buffers[3].BufferType = SECBUFFER_EMPTY;

            scRet = g_pSSPI->DecryptMessage(&hContext, &Message, 0, NULL);

            if(scRet == SEC_E_INCOMPLETE_MESSAGE)
            {
                err = recv(Socket, IoBuffer + cbIoBuffer, IO_BUFFER_SIZE - cbIoBuffer, 0);
                if ((err == SOCKET_ERROR) || (err == 0))
                {
                    printf("recv failed: %ld\n", GetLastError());
                    goto cleanup;
                }

                printf("\nReceived %d (request) bytes from client\n", err);

                PrintHexDump(16, IoBuffer+cbIoBuffer);

                cbIoBuffer += err;
            }
        }
        while(scRet == SEC_E_INCOMPLETE_MESSAGE);

        if(scRet == SEC_I_CONTEXT_EXPIRED)
        {
            // Клиент закрыл сессию
            goto cleanup;
        }

        if(scRet != SEC_E_OK)
        {
            printf("Couldn't decrypt, error %lx\n", scRet);
            goto cleanup;
        }
        cbIoBuffer = 0;

        // Расположение буфера данных.
        pDataBuffer  = NULL;
        for(i = 1; i < 4; i++)
        {
            if(Buffers[i].BufferType == SECBUFFER_DATA)
            {
                pDataBuffer = &Buffers[i];
                break;
            }
        }
        if(pDataBuffer == NULL)
        {
            goto cleanup;
        }

        // Проверка того, что данные в выходном буфере являются строкой,
        // заканчивающейся нулем, а также вывод этих данных.
        ((CHAR *) pDataBuffer->pvBuffer)[pDataBuffer->cbBuffer] = '\0';
        printf("\nMessage is: '%s'\n", pDataBuffer->pvBuffer);

        // Обработка запроса для определения объекта запроса.

        if(!ParseRequest(
                        pDataBuffer->pvBuffer,
                        pDataBuffer->cbBuffer,
                        objectName+currentDirectoryLength,
                        &cbContentLength))
        {
            printf("Unable to parse message\n");
            goto cleanup;
        }

        objectHandle = CreateFileA(
                           objectName,
                           GENERIC_READ,
                           FILE_SHARE_READ,
                           NULL,
                           OPEN_EXISTING,
                           0,
                           NULL);
        if (objectHandle == INVALID_HANDLE_VALUE)
        {
            printf("CreateFile(%s) failed: %ld\n", objectName, GetLastError());
            goto cleanup;
        }

        // Определение длины файла.

        if(!GetFileInformationByHandle(objectHandle, &fileInfo))
        {
            printf("GetFileInformationByHandle failed: %ld\n", GetLastError());
            goto cleanup;
        }

        //
        // Построение заголовка и отклик HTTP.
        //

        ZeroMemory(IoBuffer, Sizes.cbHeader);

        i = sprintf(
            IoBuffer + Sizes.cbHeader,
            "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n",
            fileInfo.nFileSizeLow);

        //
        // Построение буферов таким образом, чтобы заголовок и содержимое
        // файла полностью в них поместились.
        //

        Buffers[0].pvBuffer = IoBuffer;
        Buffers[0].cbBuffer = Sizes.cbHeader;
        Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;

        Buffers[1].pvBuffer = IoBuffer + Sizes.cbHeader;
        Buffers[1].cbBuffer = i;
        Buffers[1].BufferType = SECBUFFER_DATA;

        Buffers[2].pvBuffer = IoBuffer + Sizes.cbHeader + i;
        Buffers[2].cbBuffer = Sizes.cbTrailer;
        Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;

        Buffers[3].BufferType = SECBUFFER_EMPTY;

        scRet = g_pSSPI->EncryptMessage(&hContext, 0, &Message, 0);

        if ( FAILED( scRet ) )
        {
            printf(" EncryptMessage failed with 0x%x\n", scRet );
            goto cleanup;
        }

        err = send( Socket,
                    IoBuffer,
                    Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer,
                    0 );

        printf("\nSend %d header bytes to client\n", Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer);
        PrintHexDump(16, IoBuffer);

        if ( err == SOCKET_ERROR )
        {
            printf( "send failed: %ld\n", GetLastError( ) );
            goto cleanup;
        }

        //
        // Чтение и посылка данных из файла.
        //

        for(bytesSent = 0;
            bytesSent < (INT) fileInfo.nFileSizeLow;
            bytesSent += err)
        {

            if(!ReadFile(objectHandle,
                          IoBuffer + Sizes.cbHeader,
                          IO_BUFFER_SIZE - (Sizes.cbHeader + Sizes.cbTrailer),
                          &bytesRead,
                          NULL))
            {
                printf( "ReadFile failed: %ld\n", GetLastError( ) );
                break;
            }

            if(bytesRead == 0)
            {
                printf( "zero bytes read\n");
                break;
            }

            Buffers[0].pvBuffer = IoBuffer;
            Buffers[0].cbBuffer = Sizes.cbHeader;
            Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;

            Buffers[1].pvBuffer = IoBuffer + Sizes.cbHeader;
            Buffers[1].cbBuffer = bytesRead;
            Buffers[1].BufferType = SECBUFFER_DATA;

            Buffers[2].pvBuffer = IoBuffer + Sizes.cbHeader + bytesRead;
            Buffers[2].cbBuffer = Sizes.cbTrailer;
            Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;

            Buffers[3].BufferType = SECBUFFER_EMPTY;

            scRet = g_pSSPI->EncryptMessage(&hContext,
                                0,
                                &Message,
                                0);

            if ( FAILED( scRet ) )
            {
                printf(" EncryptMessage failed with 0x%x\n", scRet );
                goto cleanup;
            }

            err = send( Socket,
                        IoBuffer,
                        Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer,
                        0 );

            printf("\nSend %d data bytes to client\n", Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer);
                PrintHexDump(16, IoBuffer);

            if ( err == SOCKET_ERROR )
            {
                printf( "send failed: %ld\n", GetLastError( ) );
                break;
            }
        }

cleanup:

        if(fImpersonating)
        {
            RevertToSelf();
            fImpersonating = FALSE;
        }

        if(hUserToken)
        {
            CloseHandle(hUserToken);
            hUserToken = NULL;
        }

        if(fContextInitialized)
        {
            scRet = DisconnectFromClient(Socket, phServerCreds, &hContext);

            if(scRet == SEC_E_OK)
            {
                fContextInitialized = FALSE;
                Socket = INVALID_SOCKET;
            }
            else
            {
                printf("Error disconnecting from server\n");
            }
        }

        if(objectHandle != INVALID_HANDLE_VALUE)
        {
            CloseHandle(objectHandle);
        }

        // Освобождение дескриптора SSPI контекста.
        if(fContextInitialized)
        {
            g_pSSPI->DeleteSecurityContext(&hContext);
            fContextInitialized = FALSE;
        }

        // Закрытие сокета.
        if(Socket != INVALID_SOCKET)
        {
            closesocket(Socket);
            Socket = INVALID_SOCKET;
        }
    }

} // WebServer

//-------------------------------------------------------------
// Функция установления связи.

static
BOOL
SSPINegotiateLoop(
    SOCKET          Socket,
    PCtxtHandle     phContext,
    PCredHandle     phCred,
    BOOL            fDoInitialRead,
    BOOL            NewContext)
{
    TimeStamp            tsExpiry;
    SECURITY_STATUS      scRet;
    SecBufferDesc        InBuffer;
    SecBufferDesc        OutBuffer;
    SecBuffer            InBuffers[2];
    SecBuffer            OutBuffers[1];
    DWORD                err = 0;

    BOOL                 fDoRead;
    BOOL                 fInitContext = NewContext;

    DWORD                dwSSPIFlags, dwSSPIOutFlags;

    fDoRead = fDoInitialRead;

    dwSSPIFlags =   ASC_REQ_SEQUENCE_DETECT        |
                    ASC_REQ_REPLAY_DETECT      |
                    ASC_REQ_CONFIDENTIALITY  |
                    ASC_REQ_EXTENDED_ERROR    |
                    ASC_REQ_ALLOCATE_MEMORY  |
                    ASC_REQ_STREAM;

    //
    //  Установка OutBuffer для вызова InitializeSecurityContext
    //

    OutBuffer.cBuffers = 1;
    OutBuffer.pBuffers = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    scRet = SEC_I_CONTINUE_NEEDED;

    while( scRet == SEC_I_CONTINUE_NEEDED ||
            scRet == SEC_E_INCOMPLETE_MESSAGE ||
            scRet == SEC_I_INCOMPLETE_CREDENTIALS)
    {

        if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            if(fDoRead)
            {
                err = recv(Socket, IoBuffer+cbIoBuffer, IO_BUFFER_SIZE, 0);

                if (err == SOCKET_ERROR || err == 0)
                {
                    printf(" recv failed: %d\n", GetLastError() );
                    return FALSE;
                }
                else
                {
                    printf("\nReceived %d (handshake) bytes from client\n", err);
                        PrintHexDump(min(16, err), IoBuffer+cbIoBuffer);

                    cbIoBuffer += err;
                }
            }
            else
            {
                fDoRead = TRUE;
            }
        }

        //
        // InBuffers[1] используется для получения дополнительных данные,
        //  которые не были обработаны SSPI/SCHANNEL на данном шаге цикла.
        //

        InBuffers[0].pvBuffer = IoBuffer;
        InBuffers[0].cbBuffer = cbIoBuffer;
        InBuffers[0].BufferType = SECBUFFER_TOKEN;

        InBuffers[1].pvBuffer   = NULL;
        InBuffers[1].cbBuffer   = 0;
        InBuffers[1].BufferType = SECBUFFER_EMPTY;

        InBuffer.cBuffers        = 2;
        InBuffer.pBuffers        = InBuffers;
        InBuffer.ulVersion       = SECBUFFER_VERSION;

        //
        // Инициализация производится таким образом, чтобы pvBuffer содержал NULL.
        // Это сделано для того, чтобы
        // в случае неудачи не было необходимости выполнять
        // освобождение памяти.
        //

        OutBuffers[0].pvBuffer   = NULL;
        OutBuffers[0].BufferType = SECBUFFER_TOKEN;
        OutBuffers[0].cbBuffer   = 0;

        scRet = g_pSSPI->AcceptSecurityContext(
                        phCred,
                        (fInitContext?NULL:phContext),
                        &InBuffer,
                        dwSSPIFlags,
                        SECURITY_NATIVE_DREP,
                        (fInitContext?phContext:NULL),
                        &OutBuffer,
                        &dwSSPIOutFlags,
                        &tsExpiry);

        fInitContext = FALSE;

        if ( scRet == SEC_E_OK ||
             scRet == SEC_I_CONTINUE_NEEDED ||
             (FAILED(scRet) && (0 != (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))))
        {
            if  (OutBuffers[0].cbBuffer != 0    &&
                 OutBuffers[0].pvBuffer != NULL )
            {
                //
                // Посылка ответа серверу
                //
                err = send( Socket,
                            OutBuffers[0].pvBuffer,
                            OutBuffers[0].cbBuffer,
                            0 );

                printf("\nSend %d handshake bytes to client\n", OutBuffers[0].cbBuffer);
                    PrintHexDump(16, OutBuffers[0].pvBuffer);

                g_pSSPI->FreeContextBuffer( OutBuffers[0].pvBuffer );
                OutBuffers[0].pvBuffer = NULL;
            }
        }

        if ( scRet == SEC_E_OK )
        {

            if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
            {

                    memcpy(IoBuffer,
                           (LPBYTE) (IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer)),
                            InBuffers[1].cbBuffer);
                    cbIoBuffer = InBuffers[1].cbBuffer;
            }
            else
            {
                cbIoBuffer = 0;
            }

            return TRUE;
        }
        else if (FAILED(scRet) && (scRet != SEC_E_INCOMPLETE_MESSAGE))
        {

            printf("Accept Security Context Failed with error code %lx\n", scRet);
            return FALSE;

        }

        if ( scRet != SEC_E_INCOMPLETE_MESSAGE &&
             scRet != SEC_I_INCOMPLETE_CREDENTIALS)
        {

            if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
            {

                memcpy(IoBuffer,
                       (LPBYTE) (IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer)),
                        InBuffers[1].cbBuffer);
                cbIoBuffer = InBuffers[1].cbBuffer;
            }
            else
            {
                //
                // подготовка к следующему получению данных
                //

                cbIoBuffer = 0;
            }
        }
    }

    return FALSE;
} //SSPINegotiateLoop

//-------------------------------------------------------------
// Функция обработки запроса.
static
BOOL
ParseRequest (
    IN PCHAR InputBuffer,
    IN INT InputBufferLength,
    OUT PCHAR ObjectName,
    OUT DWORD *pcbContentLength)
{
    PCHAR s = InputBuffer;
    DWORD i;

    *pcbContentLength = 0;

    while ( (INT)(s - InputBuffer) < InputBufferLength )
    {

        // Обработка команды

        //
        // Первоначально определяется начинается ли данная строчка
        // с командны GET.
        //

        while(*s != '\0' && *s != ' ' && *s != '\t')
        {
            *s = (CHAR)toupper(*s);
            s++;
        }

        if(*s == '\0' || *s == ' ' || *s == '\0')
        {
            *s = '\0';
//            printf("Verb is :%s\n", InputBuffer);

            //
            // Это команда GET. Пропуск пробелов и символов табуляции.
            //
            for ( s++; *s == ' ' || *s == '\t'; s++ );

            //
            // Получение имени объекта.
            //

            for ( i = 0; *s != 0xA && *s != 0xD && *s != ' ' && *s != '\0'; s++, i++ ) {
                ObjectName[i] = *s;
                if ( ObjectName[i] == '/' ) {
                    ObjectName[i] = '\\';
                }
            }

            ObjectName[i] = '\0';

            //
            // Разбор завершен.
            //

            if(strcmp(ObjectName, "\\") == 0)
            {
                strcpy(ObjectName, "\\default.html");
            }
            if(strcmp(InputBuffer, "POST") == 0)
            {
                char * content_length;
                DWORD cbContent = 0;
                // Поиск длины содержимого;
                content_length = strstr(s, "Content-Length: ");
                if(content_length)
                {
                    cbContent = atoi(content_length+16);
                    printf("Content Length is %d\n", cbContent);
                    *pcbContentLength = cbContent;
                }
            }
            return TRUE;
        }

        //
        // Пропус концы строки и продолжение разбора.
        //

        while ( *s != 0xA && *s != 0xD )
        {
            s++;
        }

        s++;

        if ( *s == 0xD || *s == 0xA )
        {
            s++;
        }
    }

    return FALSE;

} // ParseRequest

//-------------------------------------------------------------
// Функция создания мандатов.
static
HRESULT
CreateCredentials(
    LPSTR pszUserName,              // in
    PCredHandle phCreds)            // out
{
    SCHANNEL_CRED   SchannelCred;
    TimeStamp       tsExpiry;
    SECURITY_STATUS Status;
    PCCERT_CONTEXT  pCertContext = NULL;

    if(pszUserName == NULL || strlen(pszUserName) == 0)
    {
        printf("**** No user name specified!\n");
        return SEC_E_NO_CREDENTIALS;
    }

    // Открытие хранилища сертификатов "MY".
        hMyCertStore = CertOpenSystemStore(0, "MY");

        if(!hMyCertStore)
        {
            printf("**** Error 0x%x returned by CertOpenSystemStore\n",
                GetLastError());
            return SEC_E_NO_CREDENTIALS;
        }

    // Поиск сертификата. В данном примере осуществляется поиск сертификата
    // по subject name, содержащем заданное в командной строке имя пользователя.
    pCertContext = CertFindCertificateInStore(hMyCertStore,
                                              X509_ASN_ENCODING,
                                              0,
                                              CERT_FIND_SUBJECT_STR_A,
                                              pszUserName,
                                              NULL);
    if(pCertContext == NULL)
    {
        printf("**** Error 0x%x returned by CertFindCertificateInStore\n",
            GetLastError());
        return SEC_E_NO_CREDENTIALS;
    }

    // Построение структуры Schannel мандатов.
    // В данном примере определяются используемые протокол и сертификат.

    ZeroMemory(&SchannelCred, sizeof(SchannelCred));

    SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;

    SchannelCred.cCreds = 1;
    SchannelCred.paCred = &pCertContext;

    SchannelCred.grbitEnabledProtocols = dwProtocol;

    // Создание SSPI мандата.
    Status = g_pSSPI->AcquireCredentialsHandle(
                        NULL,                   // Имя администратора
                        UNISP_NAME_A,           // Имя пакета
                        SECPKG_CRED_INBOUND,    // Флаг, определяющий использование
                        NULL,                   // Указатель на идентификатор пароля
                        &SchannelCred,          // Данные пакета
                        NULL,                   // Указатель на функицю GetKey()
                        NULL,                   // Значения, передаваемые функции GetKey()
                        phCreds,                // (out) Даскриптор мандата
                        &tsExpiry);             // (out) Период актуальности (необязательно)
    if(Status != SEC_E_OK)
    {
        printf("**** Error 0x%x returned by AcquireCredentialsHandle\n", Status);
        return Status;
    }

    // Освобождение контекста сертификата. В Schannel уже создана его копия.
    if(pCertContext)
    {
        CertFreeCertificateContext(pCertContext);
    }

    return SEC_E_OK;
} //CreateCredentials

//-------------------------------------------------------------
// Функция, выполняющая разрыв соединения с клиентом.
static
LONG
DisconnectFromClient(
    SOCKET          Socket,
    PCredHandle     phCreds,
    CtxtHandle *    phContext)
{
    DWORD           dwType;
    PCHAR           pbMessage;
    DWORD           cbMessage;
    DWORD           cbData;

    SecBufferDesc   OutBuffer;
    SecBuffer       OutBuffers[1];
    DWORD           dwSSPIFlags;
    DWORD           dwSSPIOutFlags;
    TimeStamp       tsExpiry;
    DWORD           Status;

    //
    // Уведомление schannel о закрытии соединения.
    //

    dwType = SCHANNEL_SHUTDOWN;

    OutBuffers[0].pvBuffer   = &dwType;
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].cbBuffer   = sizeof(dwType);

    OutBuffer.cBuffers  = 1;
    OutBuffer.pBuffers  = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    Status = g_pSSPI->ApplyControlToken(phContext, &OutBuffer);

    if(FAILED(Status))
    {
        printf("**** Error 0x%x returned by ApplyControlToken\n", Status);
        goto cleanup;
    }

    //
    // Построение SSL сообщения, являющегося уведомлением о закрытии.
    //

    dwSSPIFlags =   ASC_REQ_SEQUENCE_DETECT     |
                    ASC_REQ_REPLAY_DETECT       |
                    ASC_REQ_CONFIDENTIALITY     |
                    ASC_REQ_EXTENDED_ERROR      |
                    ASC_REQ_ALLOCATE_MEMORY     |
                    ASC_REQ_STREAM;

    OutBuffers[0].pvBuffer   = NULL;
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].cbBuffer   = 0;

    OutBuffer.cBuffers  = 1;
    OutBuffer.pBuffers  = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    Status = g_pSSPI->AcceptSecurityContext(
                    phCreds,
                    phContext,
                    NULL,
                    dwSSPIFlags,
                    SECURITY_NATIVE_DREP,
                    NULL,
                    &OutBuffer,
                    &dwSSPIOutFlags,
                    &tsExpiry);

    if(FAILED(Status))
    {
        printf("**** Error 0x%x returned by AcceptSecurityContext\n", Status);
        goto cleanup;
    }

    pbMessage = OutBuffers[0].pvBuffer;
    cbMessage = OutBuffers[0].cbBuffer;

    //
    // Посылка этого сообщения клиенту.
    //

    if(pbMessage != NULL && cbMessage != 0)
    {
        cbData = send(Socket, pbMessage, cbMessage, 0);
        if(cbData == SOCKET_ERROR || cbData == 0)
        {
            Status = WSAGetLastError();
            printf("**** Error %d sending close notify\n", Status);
            goto cleanup;
        }

        printf("\n%d bytes of handshake data sent\n", cbData);

            PrintHexDump(min(16, cbData), pbMessage);

        // Освобождение выходного буфера.
        g_pSSPI->FreeContextBuffer(pbMessage);
    }

cleanup:

    // Освобождение закрытого контекста.
    g_pSSPI->DeleteSecurityContext(phContext);

    // Закрытие сокета.
    closesocket(Socket);

    return Status;
} //DisconnectFromClient

//-------------------------------------------------------------
// Функция перевода в 16-ую систему исчесления.

static void
PrintHexDump(DWORD length, PCHAR buffer)
{
    DWORD i,count,index;
    CHAR rgbDigits[]="0123456789abcdef";
    CHAR rgbLine[100];
    char cbLine;

    for(index = 0; length; length -= count, buffer += count, index += count)
    {
        count = (length > 16) ? 16:length;

        sprintf(rgbLine, "%4.4x  ",index);
        cbLine = 6;

        for(i=0;i<count;i++)
        {
            rgbLine[cbLine++] = rgbDigits[buffer[i] >> 4];
            rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f];
            if(i == 7)
            {
                rgbLine[cbLine++] = ':';
            }
            else
            {
                rgbLine[cbLine++] = ' ';
            }
        }
        for(; i < 16; i++)
        {
            rgbLine[cbLine++] = ' ';
            rgbLine[cbLine++] = ' ';
            rgbLine[cbLine++] = ' ';
        }

        rgbLine[cbLine++] = ' ';

        for(i = 0; i < count; i++)
        {
            if(buffer[i] < 32 || buffer[i] > 126 || buffer[i] == '%')
            {
                rgbLine[cbLine++] = '.';
            }
            else
            {
                rgbLine[cbLine++] = buffer[i];
            }
        }

        rgbLine[cbLine++] = 0;
        printf("%s\n", rgbLine);
    }
} // Конец PrintHexDump

    Требования:

    Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
    Solaris: 9 Update 4 или выше.
    FreeBSD: FreeBSD 5.2 или выше
    Linux: RedHat 7.3, RedHad 9.0.
    Файл описания: Прототип описан в файле sspi.h.


Крипто-Про SSPI

Данный пример демонстрирует установление соединения клиента с web сервером по защищенному каналу.

Этот пример демонстрирует использование следующих функций

  • QueryContextAttributes
  • EncryptMessage
  • DecryptMessage
  • AcquireCredentialsHandle
  • FreeCredentialsHandle
  • InitializeSecurityContext
  • DeleteSecurityContext
  • FreeContextBuffer
  • ApplyControlToken

//--------------------------------------------------------------------
// В данном примере устанавливается соединение клиента с web сервером
// по защищенному каналу.
//--------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <schannel.h>

#define SECURITY_WIN32
#include <security.h>
#include <sspi.h>
#ifndef _WIN32
#include <CSP_SChannel.h>
#endif
#include <WinCryptEx.h>

#define IO_BUFFER_SIZE  0x10000

#define NT4_DLL_NAME TEXT("Security.dll")

LPSTR   pszProxyServer  = "proxy";
INT     iProxyPort      = 80;

// Опции пользователя.
LPSTR   pszServerName   = NULL;
INT     iPortNumber     = 443;
LPSTR   pszFileName     = "default.htm";
DWORD   dwProtocol      = 0;

HCERTSTORE      hMyCertStore = NULL;
SCHANNEL_CRED   SchannelCred;

HMODULE g_hSecurity = NULL;

PSecurityFunctionTable g_pSSPI;

static
SECURITY_STATUS
CreateCredentials(
    PCredHandle phCreds);

static INT
ConnectToServer(
    LPSTR pszServerName,
    INT   iPortNumber,
    SOCKET *pSocket);

static
SECURITY_STATUS
PerformClientHandshake(
    SOCKET          Socket,
    PCredHandle     phCreds,
    LPSTR           pszServerName,
    CtxtHandle *    phContext,
    SecBuffer *     pExtraData);

static
SECURITY_STATUS
ClientHandshakeLoop(
    SOCKET          Socket,
    PCredHandle     phCreds,
    CtxtHandle *    phContext,
    BOOL            fDoInitialRead,
    SecBuffer *     pExtraData);

static
SECURITY_STATUS
HttpsGetFile(
    SOCKET          Socket,
    PCredHandle     phCreds,
    CtxtHandle *    phContext,
    LPSTR           pszFileName);

static
void
DisplayCertChain(
    PCCERT_CONTEXT  pServerCert,
    BOOL            fLocal);

static
DWORD
VerifyServerCertificate(
    PCCERT_CONTEXT  pServerCert,
    PSTR            pszServerName,
    DWORD           dwCertFlags);

static
LONG
DisconnectFromServer(
    SOCKET          Socket,
    PCredHandle     phCreds,
    CtxtHandle *    phContext);

static void
DisplayConnectionInfo(
    CtxtHandle *phContext);

static void
GetNewClientCredentials(
    CredHandle *phCreds,
    CtxtHandle *phContext);

//-------------------------------------------------------------
// Функция вывода информации об использовании данного примера.
void Usage(void)
{
    printf("\n");
    printf("USAGE: webclient -s<server> \n");
    printf("\n");
    printf("    -s<server>      DNS name of server.\n");
}//Usage

//-------------------------------------------------------------
// Функция загрузки библиотеки.
BOOL
LoadSecurityLibrary(void)
{
    INIT_SECURITY_INTERFACE         pInitSecurityInterface;
    char lpszDLL[MAX_PATH];

    //
    //  Определение используемой библиотеки
    //

    strcpy (lpszDLL, NT4_DLL_NAME );

    //
    //  Загрузка библиотеки
    //

    g_hSecurity = LoadLibrary(lpszDLL);
    if(g_hSecurity == NULL)
    {
        printf("Error 0x%x loading %s.\n", GetLastError(), lpszDLL);
        return FALSE;
    }

    pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(
                                    g_hSecurity,
                                    "InitSecurityInterfaceA");

    if(pInitSecurityInterface == NULL)
    {
        printf("Error 0x%x reading InitSecurityInterface entry point.\n",
               GetLastError());
        return FALSE;
    }

    g_pSSPI = pInitSecurityInterface();

    if(g_pSSPI == NULL)
    {
        printf("Error 0x%x reading security interface.\n",
               GetLastError());
        return FALSE;
    }

    return TRUE;
}//LoadSecurityLibrary

//-------------------------------------------------------------
// Функция разгрузки библиотеки.

void
UnloadSecurityLibrary(void)
{
    FreeLibrary(g_hSecurity);
    g_hSecurity = NULL;
} //UnloadSecurityLibrary

//-------------------------------------------------------------
// Функция main.

void _cdecl main(int argc, char *argv[])
{
    WSADATA WsaData;
    SOCKET  Socket = INVALID_SOCKET;

    CredHandle hClientCreds;
    CtxtHandle hContext;
    BOOL fCredsInitialized = FALSE;
    BOOL fContextInitialized = FALSE;

    SecBuffer  ExtraData;
    SECURITY_STATUS Status;

    PCCERT_CONTEXT pRemoteCertContext = NULL;

    INT i;
    INT iOption;
    PCHAR pszOption;

    //
    // Разбор командной строки.
    //

    if(argc <= 1)
    {
        Usage();
        return;
    }

    for(i = 1; i < argc; i++)
    {
        if(argv[i][0] == '/') argv[i][0] = '-';

        if(argv[i][0] != '-')
        {
            printf("**** Invalid argument \"%s\"\n", argv[i]);
            Usage();
            return;
        }

        iOption = argv[i][1];
        pszOption = &argv[i][2];

        switch(iOption)
        {
        case 's':
            pszServerName = pszOption;
            break;

        default:
            printf("**** Invalid option \"%s\"\n", argv[i]);
            Usage();
            return;
        }
    }

    if(!LoadSecurityLibrary())
    {
        printf("Error initializing the security library\n");
        goto cleanup;
    }

    //
    // Инициализация подсистемы WinSock.
    //

    if(WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
    {
        printf("Error %d returned by WSAStartup\n", GetLastError());
        goto cleanup;
    }

    //
    // Создание мандатов.
    //

    if(CreateCredentials(&hClientCreds))
    {
        printf("Error creating credentials\n");
        goto cleanup;
    }
    fCredsInitialized = TRUE;

    //
    // Соединение с сервером.
    //

    if(ConnectToServer(pszServerName, iPortNumber, &Socket))
    {
        printf("Error connecting to server\n");
        goto cleanup;
    }

        //
        // Установление связи
        //
    if(PerformClientHandshake(Socket,
                              &hClientCreds,
                              pszServerName,
                              &hContext,
                              &ExtraData))
    {
        printf("Error performing handshake\n");
        goto cleanup;
    }
    fContextInitialized = TRUE;

    //
    // Аутентификация мандатов сервера.
    //

    // Получение сертификата сервера.
    Status = g_pSSPI->QueryContextAttributes(&hContext,
                                             SECPKG_ATTR_REMOTE_CERT_CONTEXT,
                                             (PVOID)&pRemoteCertContext);
    if(Status != SEC_E_OK)
    {
        printf("Error 0x%x querying remote certificate\n", Status);
        goto cleanup;
    }

    // Вывод цепочки сертификатов сервера.
    DisplayCertChain(pRemoteCertContext, FALSE);

    // Проверка действительности сертификата сервера.
    Status = VerifyServerCertificate(pRemoteCertContext,
                                     pszServerName,
                                     0);
    if(Status)
    {
        // Сертификат сервера не действителен. Возможно было осуществлено
        // соединение с недопустимым сервером, либо производилась атака
        // "противник посередине".

        // Лучше всего прервать соединение.

        printf("**** Error 0x%x authenticating server credentials!\n", Status);
//        goto cleanup;
    }

    // Освобождение контекста сертификата сервера.
    CertFreeCertificateContext(pRemoteCertContext);
    pRemoteCertContext = NULL;

    //
    // Вывод информации о соединении.
    //

    DisplayConnectionInfo(&hContext);

    //
    // Чтение файла с сервера.
    //

    if(HttpsGetFile(Socket,
                    &hClientCreds,
                    &hContext,
                    pszFileName))
    {
        printf("Error fetching file from server\n");
        goto cleanup;
    }

    //
    // Отправка уведомления серверу и закрытие соединения.
    //

    if(DisconnectFromServer(Socket, &hClientCreds, &hContext))
    {
        printf("Error disconnecting from server\n");
        goto cleanup;
    }
    fContextInitialized = FALSE;
    Socket = INVALID_SOCKET;

cleanup:

    // Освобождение контекста сертификата сервера.
    if(pRemoteCertContext)
    {
        CertFreeCertificateContext(pRemoteCertContext);
        pRemoteCertContext = NULL;
    }

    // Освобождение дескриптора SSPI контекста.
    if(fContextInitialized)
    {
        g_pSSPI->DeleteSecurityContext(&hContext);
        fContextInitialized = FALSE;
    }

    // Освобождение дескриптора SSPI мандатов.
    if(fCredsInitialized)
    {
        g_pSSPI->FreeCredentialsHandle(&hClientCreds);
        fCredsInitialized = FALSE;
    }

    // Закрытие сокета.
    if(Socket != INVALID_SOCKET)
    {
        closesocket(Socket);
    }

    // Завершение работы подсистемы WinSock.
    WSACleanup();

    // Закрытие хранилища сертификатов "MY".
    if(hMyCertStore)
    {
        CertCloseStore(hMyCertStore, 0);
    }

    UnloadSecurityLibrary();

    printf("Done\n");
} // main

//-------------------------------------------------------------
// Функция создания мандатов.
static
SECURITY_STATUS
CreateCredentials(
    PCredHandle phCreds)            // out
{
    TimeStamp       tsExpiry;
    SECURITY_STATUS Status;

    DWORD           cSupportedAlgs = 0;
    ALG_ID          rgbSupportedAlgs[16];

    PCCERT_CONTEXT  pCertContext = NULL;

    // Открытие хранилища сертификатов "MY" , в котором Internet Explorer
    // хранит сертификаты клиента.
    hMyCertStore = CertOpenSystemStore(0, "MY");

        if(!hMyCertStore)
        {
            printf("**** Error 0x%x returned by CertOpenSystemStore\n",
                GetLastError());
            return SEC_E_NO_CREDENTIALS;
        }

    // Построение структуры Schannel мандатов.
    // В данном примере определяются используемые протокол и сертификат.

    ZeroMemory(&SchannelCred, sizeof(SchannelCred));

    SchannelCred.dwVersion  = SCHANNEL_CRED_VERSION;
    if(pCertContext)
    {
        SchannelCred.cCreds     = 1;
        SchannelCred.paCred     = &pCertContext;
    }

    SchannelCred.grbitEnabledProtocols = dwProtocol;

    if(cSupportedAlgs)
    {
        SchannelCred.cSupportedAlgs    = cSupportedAlgs;
        SchannelCred.palgSupportedAlgs = rgbSupportedAlgs;
    }

    SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;

    // Флаг SCH_CRED_MANUAL_CRED_VALIDATION установлен, поскольку
    // поскольку в данном примере сертификат сервера проверяется "вручную".
    SchannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;

    //
    // Создание SSPI мандата.
    //

    Status = g_pSSPI->AcquireCredentialsHandleA(
                        NULL,                   // Name of principal
                        UNISP_NAME_A,           // Name of package
                        SECPKG_CRED_OUTBOUND,   // Flags indicating use
                        NULL,                   // Pointer to logon ID
                        &SchannelCred,          // Package specific data
                        NULL,                   // Pointer to GetKey() func
                        NULL,                   // Value to pass to GetKey()
                        phCreds,                // (out) Cred Handle
                        &tsExpiry);             // (out) Lifetime (optional)
    if(Status != SEC_E_OK)
    {
        printf("**** Error 0x%x returned by AcquireCredentialsHandle\n", Status);
        goto cleanup;
    }

cleanup:

    //
    // Освобождение контекста сертификата. В Schannel уже создана его копия.
    //

    if(pCertContext)
    {
        CertFreeCertificateContext(pCertContext);
    }

    return Status;
} //CreateCredentials

//-------------------------------------------------------------
// Функция, устанавливающая соединение с сервером.
static INT
ConnectToServer(
    LPSTR    pszServerName, // in
    INT      iPortNumber,   // in
    SOCKET * pSocket)       // out
{
    SOCKET Socket;
    struct sockaddr_in sin;
    struct hostent *hp;

    Socket = socket(PF_INET, SOCK_STREAM, 0);
    if(Socket == INVALID_SOCKET)
    {
        printf("**** Error %d creating socket\n", WSAGetLastError());
        return WSAGetLastError();
    }

        sin.sin_family = AF_INET;
        sin.sin_port = htons((u_short)iPortNumber);

        if((hp = gethostbyname(pszServerName)) == NULL)
        {
            printf("**** Error %d returned by gethostbyname\n", WSAGetLastError());
            return WSAGetLastError();
        }
        else
        {
            memcpy(&sin.sin_addr, hp->h_addr, 4);
        }

    if(connect(Socket, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        printf("**** Error %d connecting to \"%s\" (%s)\n",
            WSAGetLastError(),
            pszServerName,
            inet_ntoa(sin.sin_addr));
        closesocket(Socket);
        return WSAGetLastError();
    }

      *pSocket = Socket;

    return SEC_E_OK;
} //ConnectToServer

//-------------------------------------------------------------
// Функция, выполняющая разрыв соединения с сервером.

static
LONG
DisconnectFromServer(
    SOCKET          Socket,
    PCredHandle     phCreds,
    CtxtHandle *    phContext)
{
    DWORD           dwType;
    char*           pbMessage;
    DWORD           cbMessage;
    DWORD           cbData;

    SecBufferDesc   OutBuffer;
    SecBuffer       OutBuffers[1];
    DWORD           dwSSPIFlags;
    DWORD           dwSSPIOutFlags;
    TimeStamp       tsExpiry;
    DWORD           Status;

    //
    // Уведомление schannel о закрытии соединения.
    //

    dwType = SCHANNEL_SHUTDOWN;

    OutBuffers[0].pvBuffer   = &dwType;
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].cbBuffer   = sizeof(dwType);

    OutBuffer.cBuffers  = 1;
    OutBuffer.pBuffers  = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    Status = g_pSSPI->ApplyControlToken(phContext, &OutBuffer);

    if(FAILED(Status))
    {
        printf("**** Error 0x%x returned by ApplyControlToken\n", Status);
        goto cleanup;
    }

    //
    // Построение SSL сообщения, являющегося уведомлением о закрытии.
    //

    dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                  ISC_REQ_REPLAY_DETECT     |
                  ISC_REQ_CONFIDENTIALITY   |
                  ISC_RET_EXTENDED_ERROR    |
                  ISC_REQ_ALLOCATE_MEMORY   |
                  ISC_REQ_STREAM;

    OutBuffers[0].pvBuffer   = NULL;
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].cbBuffer   = 0;

    OutBuffer.cBuffers  = 1;
    OutBuffer.pBuffers  = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    Status = g_pSSPI->InitializeSecurityContextA(
                    phCreds,
                    phContext,
                    NULL,
                    dwSSPIFlags,
                    0,
                    SECURITY_NATIVE_DREP,
                    NULL,
                    0,
                    phContext,
                    &OutBuffer,
                    &dwSSPIOutFlags,
                    &tsExpiry);

    if(FAILED(Status))
    {
        printf("**** Error 0x%x returned by InitializeSecurityContext\n", Status);
        goto cleanup;
    }

    pbMessage = OutBuffers[0].pvBuffer;
    cbMessage = OutBuffers[0].cbBuffer;

    //
    // Посылка этого сообщения серверу.
    //

    if(pbMessage != NULL && cbMessage != 0)
    {
        cbData = send(Socket, pbMessage, cbMessage, 0);
        if(cbData == SOCKET_ERROR || cbData == 0)
        {
            Status = WSAGetLastError();
            printf("**** Error %d sending close notify\n", Status);
            goto cleanup;
        }

        printf("Sending Close Notify\n");
        printf("%d bytes of handshake data sent\n", cbData);

        // Освобождение выходного буфера.
        g_pSSPI->FreeContextBuffer(pbMessage);
    }

cleanup:

    // Освобождение закрытого контекста.
    g_pSSPI->DeleteSecurityContext(phContext);

    // Закрытие сокета.
    closesocket(Socket);

    return Status;
} // DisconnectFromServer

//-------------------------------------------------------------
// Функция установления связи клиента с сервером.
static
SECURITY_STATUS
PerformClientHandshake(
    SOCKET          Socket,         // in
    PCredHandle     phCreds,        // in
    LPSTR           pszServerName,  // in
    CtxtHandle *    phContext,      // out
    SecBuffer *     pExtraData)     // out
{
    SecBufferDesc   OutBuffer;
    SecBuffer       OutBuffers[1];
    DWORD           dwSSPIFlags;
    DWORD           dwSSPIOutFlags;
    TimeStamp       tsExpiry;
    SECURITY_STATUS scRet;
    DWORD           cbData;

    dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                  ISC_REQ_REPLAY_DETECT     |
                  ISC_REQ_CONFIDENTIALITY   |
                  ISC_RET_EXTENDED_ERROR    |
                  ISC_REQ_ALLOCATE_MEMORY   |
                  ISC_REQ_STREAM;

    //
    //  Инициализация сообщения ClientHello и генерация токена.
    //

    OutBuffers[0].pvBuffer   = NULL;
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].cbBuffer   = 0;

    OutBuffer.cBuffers = 1;
    OutBuffer.pBuffers = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    scRet = g_pSSPI->InitializeSecurityContextA(
                    phCreds,
                    NULL,
                    pszServerName,
                    dwSSPIFlags,
                    0,
                    SECURITY_NATIVE_DREP,
                    NULL,
                    0,
                    phContext,
                    &OutBuffer,
                    &dwSSPIOutFlags,
                    &tsExpiry);

    if(scRet != SEC_I_CONTINUE_NEEDED)
    {
        printf("**** Error %d returned by InitializeSecurityContext (1)\n", scRet);
        return scRet;
    }

    // Посылка отвера серверу.
    if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
    {
        cbData = send(Socket,
                      OutBuffers[0].pvBuffer,
                      OutBuffers[0].cbBuffer,
                      0);
        if(cbData == SOCKET_ERROR || cbData == 0)
        {
            printf("**** Error %d sending data to server (1)\n", WSAGetLastError());
            g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
            g_pSSPI->DeleteSecurityContext(phContext);
            return SEC_E_INTERNAL_ERROR;
        }

        printf("%d bytes of handshake data sent\n", cbData);

        // Освобождение выходного буфера.
        g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
        OutBuffers[0].pvBuffer = NULL;
    }

    return ClientHandshakeLoop(Socket, phCreds, phContext, TRUE, pExtraData);
} // PerformClientHandshake

//-------------------------------------------------------------
// Функция обмена сообщениями между клиентом и сервером.
static
SECURITY_STATUS
ClientHandshakeLoop(
    SOCKET          Socket,         // in
    PCredHandle     phCreds,        // in
    CtxtHandle *    phContext,      // in, out
    BOOL            fDoInitialRead, // in
    SecBuffer *     pExtraData)     // out
{
    SecBufferDesc   InBuffer;
    SecBuffer       InBuffers[2];
    SecBufferDesc   OutBuffer;
    SecBuffer       OutBuffers[1];
    DWORD           dwSSPIFlags;
    DWORD           dwSSPIOutFlags;
    TimeStamp       tsExpiry;
    SECURITY_STATUS scRet;
    DWORD           cbData;

    char*          IoBuffer;
    DWORD           cbIoBuffer;
    BOOL            fDoRead;

    dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                  ISC_REQ_REPLAY_DETECT     |
                  ISC_REQ_CONFIDENTIALITY   |
                  ISC_RET_EXTENDED_ERROR    |
                  ISC_REQ_ALLOCATE_MEMORY   |
                  ISC_REQ_STREAM;

    //
    // Размещение буфера данных.
    //

    IoBuffer = LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE);
    if(IoBuffer == NULL)
    {
        printf("**** Out of memory (1)\n");
        return SEC_E_INTERNAL_ERROR;
    }
    cbIoBuffer = 0;

    fDoRead = fDoInitialRead;

    //
    // Цикл до тех пока, пока не закончится обмен сообщениями,
    // либо не произойдет ошибка.
    //

    scRet = SEC_I_CONTINUE_NEEDED;

    while(scRet == SEC_I_CONTINUE_NEEDED        ||
          scRet == SEC_E_INCOMPLETE_MESSAGE     ||
          scRet == SEC_I_INCOMPLETE_CREDENTIALS)
   {

        //
        // Чтение данных из сервера.
        //

        if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            if(fDoRead)
            {
                cbData = recv(Socket,
                              IoBuffer + cbIoBuffer,
                              IO_BUFFER_SIZE - cbIoBuffer,
                              0);
                if(cbData == SOCKET_ERROR)
                {
                    printf("**** Error %d reading data from server\n", WSAGetLastError());
                    scRet = SEC_E_INTERNAL_ERROR;
                    break;
                }
                else if(cbData == 0)
                {
                    printf("**** Server unexpectedly disconnected\n");
                    scRet = SEC_E_INTERNAL_ERROR;
                    break;
                }

                printf("%d bytes of handshake data received\n", cbData);

                cbIoBuffer += cbData;
            }
            else
            {
                fDoRead = TRUE;
            }
        }

        //
        // Установка входных буферов. Buffer 0 содержит данные, получаемые
        // от сервера. Schannel поглощает некоторые или все из них.
        // Оставшиеся данные (в любом случае) располагаются в  buffer 1 и
        // получают тип буфера SECBUFFER_EXTRA.
        //

        InBuffers[0].pvBuffer   = IoBuffer;
        InBuffers[0].cbBuffer   = cbIoBuffer;
        InBuffers[0].BufferType = SECBUFFER_TOKEN;

        InBuffers[1].pvBuffer   = NULL;
        InBuffers[1].cbBuffer   = 0;
        InBuffers[1].BufferType = SECBUFFER_EMPTY;

        InBuffer.cBuffers       = 2;
        InBuffer.pBuffers       = InBuffers;
        InBuffer.ulVersion      = SECBUFFER_VERSION;

        //
        // Установка выходных буферов. Инициализация производится таким образом,
        // чтобы pvBuffer содержал NULL.
        // Это сделано для того, чтобы в случае неудачи не было необходимости выполнять
        // освобождение памяти.
        //

        OutBuffers[0].pvBuffer  = NULL;
        OutBuffers[0].BufferType= SECBUFFER_TOKEN;
        OutBuffers[0].cbBuffer  = 0;

        OutBuffer.cBuffers      = 1;
        OutBuffer.pBuffers      = OutBuffers;
        OutBuffer.ulVersion     = SECBUFFER_VERSION;

        //
        // Вызов InitializeSecurityContext.
        //

        scRet = g_pSSPI->InitializeSecurityContextA(phCreds,
                                          phContext,
                                          NULL,
                                          dwSSPIFlags,
                                          0,
                                          SECURITY_NATIVE_DREP,
                                          &InBuffer,
                                          0,
                                          NULL,
                                          &OutBuffer,
                                          &dwSSPIOutFlags,
                                          &tsExpiry);

        //
        // Если InitializeSecurityContext успешно выполнена (или если произошла
        // одна из распространенных ошибок), то выполняется посылка выходного
        // буфера серверу.
        //

        if(scRet == SEC_E_OK                ||
           scRet == SEC_I_CONTINUE_NEEDED   ||
           FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
        {
            if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
            {
                cbData = send(Socket,
                              OutBuffers[0].pvBuffer,
                              OutBuffers[0].cbBuffer,
                              0);
                if(cbData == SOCKET_ERROR || cbData == 0)
                {
                    printf("**** Error %d sending data to server (2)\n",
                        WSAGetLastError());
                    g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
                    g_pSSPI->DeleteSecurityContext(phContext);
                    return SEC_E_INTERNAL_ERROR;
                }

                printf("%d bytes of handshake data sent\n", cbData);

                // Освобождение выходного буфера.
                g_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
                OutBuffers[0].pvBuffer = NULL;
            }
        }

        //
        // Если InitializeSecurityContext вернула ошибку SEC_E_INCOMPLETE_MESSAGE,
        // тогда необходимо прочитать большее количество данных от сервера и повторить
        // попытку снова.
        //

        if(scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            continue;
        }

        //
        // Если InitializeSecurityContext возвратила SEC_E_OK, тогда
        // обмен данными завершился успешно.
        //

        if(scRet == SEC_E_OK)
        {
            //
            // Если "extra" буфер содержит данные, то они являются данными
            // протокола зашифрования. Их необходимо сохранить.
            // Приложение позже расшифрует их при помощи DecryptMessage.
            //

            printf("Handshake was successful\n");

            if(InBuffers[1].BufferType == SECBUFFER_EXTRA)
            {
                pExtraData->pvBuffer = LocalAlloc(LMEM_FIXED,
                                                  InBuffers[1].cbBuffer);
                if(pExtraData->pvBuffer == NULL)
                {
                    printf("**** Out of memory (2)\n");
                    return SEC_E_INTERNAL_ERROR;
                }

                MoveMemory(pExtraData->pvBuffer,
                           IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
                           InBuffers[1].cbBuffer);

                pExtraData->cbBuffer   = InBuffers[1].cbBuffer;
                pExtraData->BufferType = SECBUFFER_TOKEN;

                printf("%d bytes of app data was bundled with handshake data\n",
                    pExtraData->cbBuffer);
            }
            else
            {
                pExtraData->pvBuffer   = NULL;
                pExtraData->cbBuffer   = 0;
                pExtraData->BufferType = SECBUFFER_EMPTY;
            }

            //
            // Выход
            //

            break;
        }

        //
        // Проверка на ошибки.
        //

        if(FAILED(scRet))
        {
            printf("**** Error 0x%x returned by InitializeSecurityContext (2)\n", scRet);
            break;
        }

        //
        // Если InitializeSecurityContext возвратила SEC_I_INCOMPLETE_CREDENTIALS,
        // то сервер только что запросил аутентификацию клиента.
        //

        if(scRet == SEC_I_INCOMPLETE_CREDENTIALS)
        {
            //
            // Ошибка. Сервер запросил аутентификацию клиента, но переданный
            // мандат не содержит сертификата клиента.
            //

            //
            // Эта функция читает список доверенных сертификатов авторов
            // ("issuers"), полученный от сервера и пытается найти
            // подходящий сертификат клиента. Если эта функция выполнена успешно,
            // то происходит соединение при помощи нового сертификата.
            // В противном случае осуществляется попытка произвести
            // соединение анонимно (используя текущий мандат).
            //

            GetNewClientCredentials(phCreds, phContext);

            // Повторная попытка.
            fDoRead = FALSE;
            scRet = SEC_I_CONTINUE_NEEDED;
            continue;
        }

        //
        // Копирование всех данных из "extra" буфера и повторная попытка.
        //

        if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
        {
            MoveMemory(IoBuffer,
                       IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
                       InBuffers[1].cbBuffer);

            cbIoBuffer = InBuffers[1].cbBuffer;
        }
        else
        {
            cbIoBuffer = 0;
        }
    }

    // Уничтожение закрытого контекста в случае непоправимой ошибки.
    if(FAILED(scRet))
    {
        g_pSSPI->DeleteSecurityContext(phContext);
    }

    LocalFree(IoBuffer);

    return scRet;
} // ClientHandshakeLoop

//-------------------------------------------------------------
// Функция получения файла при помощи Https.
static
SECURITY_STATUS
HttpsGetFile(
    SOCKET          Socket,         // in
    PCredHandle     phCreds,        // in
    CtxtHandle *    phContext,      // in
    LPSTR           pszFileName)    // in
{
    SecPkgContext_StreamSizes Sizes;
    SECURITY_STATUS scRet;
    SecBufferDesc   Message;
    SecBuffer       Buffers[4];
    SecBuffer *     pDataBuffer;
    SecBuffer *     pExtraBuffer;
    SecBuffer       ExtraBuffer;

    char* pbIoBuffer;
    DWORD cbIoBuffer;
    DWORD cbIoBufferLength;
    char* pbMessage;
    DWORD cbMessage;

    DWORD cbData;
    INT   i;

    //
    // Чтение свойств поточного зашифрования.
    //

    scRet = g_pSSPI->QueryContextAttributes(phContext,
                                   SECPKG_ATTR_STREAM_SIZES,
                                   &Sizes);
    if(scRet != SEC_E_OK)
    {
        printf("**** Error 0x%x reading SECPKG_ATTR_STREAM_SIZES\n", scRet);
        return scRet;
    }

    printf("\nHeader: %d, Trailer: %d, MaxMessage: %d\n",
        Sizes.cbHeader,
        Sizes.cbTrailer,
        Sizes.cbMaximumMessage);

    //
    // Выделение рабочего буфера. Открытый текст, передаваемый EncryptMessage,
    // должен ен провосходить по размерам 'Sizes.cbMaximumMessage', поэтому
    // размер буфера равен этой величене, сложеной с размерами заголовка и
    // заключительной части.
    //

    cbIoBufferLength = Sizes.cbHeader +
                       Sizes.cbMaximumMessage +
                       Sizes.cbTrailer;

    pbIoBuffer = LocalAlloc(LMEM_FIXED, cbIoBufferLength);
    if(pbIoBuffer == NULL)
    {
        printf("**** Out of memory (2)\n");
        return SEC_E_INTERNAL_ERROR;
    }

    //
    // Построение HTTP запроса серверу.
    //

    // Удаление завершающих '/' из имени файла, должен быть только один.
    if(pszFileName &&
       strlen(pszFileName) > 1 &&
       pszFileName[strlen(pszFileName) - 1] == '/')
    {
        pszFileName[strlen(pszFileName)-1] = 0;
    }

    // Построение HTTP запроса, сдвинутого на "header size" байт в буфере.
    // Это позволяет Schannel выполнять операцию зашифрования.
    pbMessage = pbIoBuffer + Sizes.cbHeader;

    // Построение HTTP запроса. Он меньше, чем максимальный размер сообщения.
    // Если это не так, что происходит break.
    sprintf(pbMessage,
            "GET /%s HTTP/1.0\r\nUser-Agent: Webclient\r\nAccept:*/*\r\n\r\n",
            pszFileName);
    printf("\nHTTP request: %s\n", pbMessage);

    cbMessage = (DWORD)strlen(pbMessage);

    printf("Sending plaintext: %d bytes\n", cbMessage);

    //
    // Зашифрование HTTP запроса.
    //

    Buffers[0].pvBuffer     = pbIoBuffer;
    Buffers[0].cbBuffer     = Sizes.cbHeader;
    Buffers[0].BufferType   = SECBUFFER_STREAM_HEADER;

    Buffers[1].pvBuffer     = pbMessage;
    Buffers[1].cbBuffer     = cbMessage;
    Buffers[1].BufferType   = SECBUFFER_DATA;

    Buffers[2].pvBuffer     = pbMessage + cbMessage;
    Buffers[2].cbBuffer     = Sizes.cbTrailer;
    Buffers[2].BufferType   = SECBUFFER_STREAM_TRAILER;

    Buffers[3].BufferType   = SECBUFFER_EMPTY;

    Message.ulVersion       = SECBUFFER_VERSION;
    Message.cBuffers        = 4;
    Message.pBuffers        = Buffers;

    scRet = g_pSSPI->EncryptMessage(phContext, 0, &Message, 0);

    if(FAILED(scRet))
    {
        printf("**** Error 0x%x returned by EncryptMessage\n", scRet);
        return scRet;
    }

    //
    // Посылка зашифрованных данных серверу.
    //

    cbData = send(Socket,
                  pbIoBuffer,
                  Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer,
                  0);
    if(cbData == SOCKET_ERROR || cbData == 0)
    {
        printf("**** Error %d sending data to server (3)\n",
            WSAGetLastError());
        g_pSSPI->DeleteSecurityContext(phContext);
        return SEC_E_INTERNAL_ERROR;
    }

    printf("%d bytes of application data sent\n", cbData);

    //
    // Чтение данных от сервера до их окончания.
    //

    cbIoBuffer = 0;

    for(;;)
    {
        //
        // Чтение данных.
        //

        if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            cbData = recv(Socket,
                          pbIoBuffer + cbIoBuffer,
                          cbIoBufferLength - cbIoBuffer,
                          0);
            if(cbData == SOCKET_ERROR)
            {
                printf("**** Error %d reading data from server\n", WSAGetLastError());
                scRet = SEC_E_INTERNAL_ERROR;
                break;
            }
            else if(cbData == 0)
            {
                // Произошел разрыв соединения с сервером.
                if(cbIoBuffer)
                {
                    printf("**** Server unexpectedly disconnected\n");
                    scRet = SEC_E_INTERNAL_ERROR;
                    return scRet;
                }
                else
                {
                    break;
                }
            }
            else
            {
                printf("%d bytes of (encrypted) application data received\n", cbData);

                cbIoBuffer += cbData;
            }
        }

        //
        // Попытка расшифрования поченных от сервера данных.
        //

        Buffers[0].pvBuffer     = pbIoBuffer;
        Buffers[0].cbBuffer     = cbIoBuffer;
        Buffers[0].BufferType   = SECBUFFER_DATA;

        Buffers[1].BufferType   = SECBUFFER_EMPTY;
        Buffers[2].BufferType   = SECBUFFER_EMPTY;
        Buffers[3].BufferType   = SECBUFFER_EMPTY;

        Message.ulVersion       = SECBUFFER_VERSION;
        Message.cBuffers        = 4;
        Message.pBuffers        = Buffers;

        scRet = g_pSSPI->DecryptMessage(phContext, &Message, 0, NULL);

        if(scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            // Входной буфер содержит только фрагмент зашифрованных данных.
            // Продолжение цикла и чтение данных.
            continue;
        }

        // Сервер завершил сессию
        if(scRet == SEC_I_CONTEXT_EXPIRED)
            break;

        if( scRet != SEC_E_OK &&
            scRet != SEC_I_RENEGOTIATE &&
            scRet != SEC_I_CONTEXT_EXPIRED)
        {
            printf("**** Error 0x%x returned by DecryptMessage\n", scRet);
            return scRet;
        }

        // Расположение данных и (необязательно) добавление буферов.
        pDataBuffer  = NULL;
        pExtraBuffer = NULL;
        for(i = 1; i < 4; i++)
        {

            if(pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA)
            {
                pDataBuffer = &Buffers[i];
                printf("Buffers[%d].BufferType = SECBUFFER_DATA\n",i);
            }
            if(pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA)
            {
                pExtraBuffer = &Buffers[i];
            }
        }

        // Вывод или обработка расшифрованных данных.
        if(pDataBuffer)
        {
            printf("Decrypted data: %d bytes\n", pDataBuffer->cbBuffer);
        }

        // Перенос всех "extra" данных во входной буфер.
        if(pExtraBuffer)
        {
            MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
            cbIoBuffer = pExtraBuffer->cbBuffer;
        }
        else
        {
            cbIoBuffer = 0;
        }

        if(scRet == SEC_I_RENEGOTIATE)
        {
            // Сервер потребовал другой обмен сообщениями с клиентом.

            printf("Server requested renegotiate!\n");

            scRet = ClientHandshakeLoop(Socket,
                                        phCreds,
                                        phContext,
                                        FALSE,
                                        &ExtraBuffer);
            if(scRet != SEC_E_OK)
            {
                return scRet;
            }

            // Перенос всех "extra" данных во входной буфер.
            if(ExtraBuffer.pvBuffer)
            {
                MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer);
                cbIoBuffer = ExtraBuffer.cbBuffer;
            }
        }
    }

    return SEC_E_OK;
} // HttpsGetFile

//-------------------------------------------------------------
// Функция вывода цепочки сертификатов.
static
void
DisplayCertChain(
    PCCERT_CONTEXT  pServerCert,
    BOOL            fLocal)
{
    CHAR szName[1000];
    PCCERT_CONTEXT pCurrentCert;
    PCCERT_CONTEXT pIssuerCert;
    DWORD dwVerificationFlags;

    printf("\n");

    // Вывод листа цеточки
    if(!CertNameToStr(pServerCert->dwCertEncodingType,
                      &pServerCert->pCertInfo->Subject,
                      CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
                      szName, sizeof(szName)))
    {
        printf("**** Error 0x%x building subject name\n", GetLastError());
    }
    if(fLocal)
    {
        printf("Client subject: %s\n", szName);
    }
    else
    {
        printf("Server subject: %s\n", szName);
    }
    if(!CertNameToStr(pServerCert->dwCertEncodingType,
                      &pServerCert->pCertInfo->Issuer,
                      CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
                      szName, sizeof(szName)))
    {
        printf("**** Error 0x%x building issuer name\n", GetLastError());
    }
    if(fLocal)
    {
        printf("Client issuer: %s\n", szName);
    }
    else
    {
        printf("Server issuer: %s\n\n", szName);
    }

    // Вывод цепочки сертификатов
    pCurrentCert = pServerCert;
    while(pCurrentCert != NULL)
    {
        dwVerificationFlags = 0;
        pIssuerCert = CertGetIssuerCertificateFromStore(pServerCert->hCertStore,
                                                        pCurrentCert,
                                                        NULL,
                                                        &dwVerificationFlags);
        if(pIssuerCert == NULL)
        {
            if(pCurrentCert != pServerCert)
            {
                CertFreeCertificateContext(pCurrentCert);
            }
            break;
        }

        if(!CertNameToStr(pIssuerCert->dwCertEncodingType,
                          &pIssuerCert->pCertInfo->Subject,
                          CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
                          szName, sizeof(szName)))
        {
            printf("**** Error 0x%x building subject name\n", GetLastError());
        }
        printf("CA subject: %s\n", szName);
        if(!CertNameToStr(pIssuerCert->dwCertEncodingType,
                          &pIssuerCert->pCertInfo->Issuer,
                          CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
                          szName, sizeof(szName)))
        {
            printf("**** Error 0x%x building issuer name\n", GetLastError());
        }
        printf("CA issuer: %s\n\n", szName);

        if(pCurrentCert != pServerCert)
        {
            CertFreeCertificateContext(pCurrentCert);
        }
        pCurrentCert = pIssuerCert;
        pIssuerCert = NULL;
    }
} // DisplayCertChain

//-------------------------------------------------------------
// Функция вывода ошибки полученной при проверке цепочки сертификатов.

static
void
DisplayWinVerifyTrustError(DWORD Status)
{
    LPSTR pszName = NULL;

    switch(Status)
    {
    case CERT_E_EXPIRED:                pszName = "CERT_E_EXPIRED";                 break;
    case CERT_E_VALIDITYPERIODNESTING:  pszName = "CERT_E_VALIDITYPERIODNESTING";   break;
    case CERT_E_ROLE:                   pszName = "CERT_E_ROLE";                    break;
    case CERT_E_PATHLENCONST:           pszName = "CERT_E_PATHLENCONST";            break;
    case CERT_E_CRITICAL:               pszName = "CERT_E_CRITICAL";                break;
    case CERT_E_PURPOSE:                pszName = "CERT_E_PURPOSE";                 break;
    case CERT_E_ISSUERCHAINING:         pszName = "CERT_E_ISSUERCHAINING";          break;
    case CERT_E_MALFORMED:              pszName = "CERT_E_MALFORMED";               break;
    case CERT_E_UNTRUSTEDROOT:          pszName = "CERT_E_UNTRUSTEDROOT";           break;
    case CERT_E_CHAINING:               pszName = "CERT_E_CHAINING";                break;
    case TRUST_E_FAIL:                  pszName = "TRUST_E_FAIL";                   break;
    case CERT_E_REVOKED:                pszName = "CERT_E_REVOKED";                 break;
    case CERT_E_UNTRUSTEDTESTROOT:      pszName = "CERT_E_UNTRUSTEDTESTROOT";       break;
    case CERT_E_REVOCATION_FAILURE:     pszName = "CERT_E_REVOCATION_FAILURE";      break;
    case CERT_E_CN_NO_MATCH:            pszName = "CERT_E_CN_NO_MATCH";             break;
    case CERT_E_WRONG_USAGE:            pszName = "CERT_E_WRONG_USAGE";             break;
    default:                            pszName = "(unknown)";                      break;
    }

    printf("Error 0x%x (%s) returned by CertVerifyCertificateChainPolicy!\n",
        Status, pszName);
} //DisplayWinVerifyTrustError

//-------------------------------------------------------------
// Функция проверки сертификата сервера.
static
DWORD
VerifyServerCertificate(
    PCCERT_CONTEXT  pServerCert,
    PSTR            pszServerName,
    DWORD           dwCertFlags)
{
    HTTPSPolicyCallbackData  polHttps;
    CERT_CHAIN_POLICY_PARA   PolicyPara;
    CERT_CHAIN_POLICY_STATUS PolicyStatus;
    CERT_CHAIN_PARA          ChainPara;
    PCCERT_CHAIN_CONTEXT     pChainContext = NULL;

    LPSTR rgszUsages[] = {  szOID_PKIX_KP_SERVER_AUTH,
                            szOID_SERVER_GATED_CRYPTO,
                            szOID_SGC_NETSCAPE };
    DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);

    PWSTR   pwszServerName = NULL;
    DWORD   cchServerName;
    HRESULT   Status;

    if(pServerCert == NULL)
    {
        Status = SEC_E_WRONG_PRINCIPAL;
        goto cleanup;
    }

    //
    // Преобразование имени сервера в unicode.
    //

    if(pszServerName == NULL || strlen(pszServerName) == 0)
    {
        Status = SEC_E_WRONG_PRINCIPAL;
        goto cleanup;
    }

    cchServerName = MultiByteToWideChar(CP_ACP, 0, pszServerName, -1, NULL, 0);
    pwszServerName = LocalAlloc(LMEM_FIXED, cchServerName * sizeof(WCHAR));
    if(pwszServerName == NULL)
    {
        Status = SEC_E_INSUFFICIENT_MEMORY;
        goto cleanup;
    }
    cchServerName = MultiByteToWideChar(CP_ACP, 0, pszServerName, -1, pwszServerName, cchServerName);
    if(cchServerName == 0)
    {
        Status = SEC_E_WRONG_PRINCIPAL;
        goto cleanup;
    }

    //
    // Построение цепочки сертификатов.
    //

    ZeroMemory(&ChainPara, sizeof(ChainPara));
    ChainPara.cbSize = sizeof(ChainPara);
    ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
    ChainPara.RequestedUsage.Usage.cUsageIdentifier     = cUsages;
    ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;

    if(!CertGetCertificateChain(
                            NULL,
                            pServerCert,
                            NULL,
                            pServerCert->hCertStore,
                            &ChainPara,
                            0,
                            NULL,
                            &pChainContext))
    {
        Status = GetLastError();
        printf("Error 0x%x returned by CertGetCertificateChain!\n", Status);
        goto cleanup;
    }

    //
    // Проверка цепочки сертификатов.
    //

    ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
    polHttps.cbStruct           = sizeof(HTTPSPolicyCallbackData);
    polHttps.dwAuthType         = AUTHTYPE_SERVER;
    polHttps.fdwChecks          = dwCertFlags;
    polHttps.pwszServerName     = pwszServerName;

    memset(&PolicyPara, 0, sizeof(PolicyPara));
    PolicyPara.cbSize            = sizeof(PolicyPara);
    PolicyPara.pvExtraPolicyPara = &polHttps;

    memset(&PolicyStatus, 0, sizeof(PolicyStatus));
    PolicyStatus.cbSize = sizeof(PolicyStatus);

    if(!CertVerifyCertificateChainPolicy(
                            CERT_CHAIN_POLICY_SSL,
                            pChainContext,
                            &PolicyPara,
                            &PolicyStatus))
    {
        Status = GetLastError();
        printf("Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", Status);
        goto cleanup;
    }

    if(PolicyStatus.dwError)
    {
        Status = PolicyStatus.dwError;
        DisplayWinVerifyTrustError(Status);
        goto cleanup;
    }

    Status = SEC_E_OK;

cleanup:

    if(pChainContext)
    {
        CertFreeCertificateChain(pChainContext);
    }

    if(pwszServerName)
    {
        LocalFree(pwszServerName);
    }

    return Status;
} // VerifyServerCertificate

//-------------------------------------------------------------
// Функция вывода информации о соединении.

static
void
DisplayConnectionInfo(
    CtxtHandle *phContext)
{
    SECURITY_STATUS Status;
    SecPkgContext_ConnectionInfo ConnectionInfo;

    Status = g_pSSPI->QueryContextAttributes(phContext,
                                    SECPKG_ATTR_CONNECTION_INFO,
                                    (PVOID)&ConnectionInfo);
    if(Status != SEC_E_OK)
    {
        printf("Error 0x%x querying connection info\n", Status);
        return;
    }

    printf("\n");

    switch(ConnectionInfo.dwProtocol)
    {
        case SP_PROT_TLS1_CLIENT:
            printf("Protocol: TLS1\n");
            break;

        default:
            printf("Protocol: 0x%x\n", ConnectionInfo.dwProtocol);
    }

    switch(ConnectionInfo.aiCipher)
    {
        case CALG_G28147:
            printf("Cipher: Gost 28147-89\n");
            break;

        default:
            printf("Cipher: 0x%x\n", ConnectionInfo.aiCipher);
    }

    printf("Cipher strength: %d\n", ConnectionInfo.dwCipherStrength);

    switch(ConnectionInfo.aiHash)
    {
        case CALG_GR3411:
            printf("Hash: Gost R 34.11-94\n");
            break;

        default:
            printf("Hash: 0x%x\n", ConnectionInfo.aiHash);
    }

    printf("Hash strength: %d\n", ConnectionInfo.dwHashStrength);

    switch(ConnectionInfo.aiExch)
    {
        case CALG_DH_EX_EPHEM:
            printf("Key exchange: DH Ephemeral (Gost R 34.10-94)\n");
            break;

        default:
            printf("Key exchange: 0x%x\n", ConnectionInfo.aiExch);
    }

    printf("Key exchange strength: %d\n", ConnectionInfo.dwExchStrength);
} //DisplayConnectionInfo

//-------------------------------------------------------------
// Функция получения пновых мандатов клиента.

static
void
GetNewClientCredentials(
    CredHandle *phCreds,
    CtxtHandle *phContext)
{
    CredHandle hCreds;
    SecPkgContext_IssuerListInfoEx IssuerListInfo;
    PCCERT_CHAIN_CONTEXT pChainContext;
    CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
    PCCERT_CONTEXT  pCertContext;
    TimeStamp       tsExpiry;
    SECURITY_STATUS Status;

    //
    // Чтение спсика доверенных издателей из schannel.
    //

    Status = g_pSSPI->QueryContextAttributes(phContext,
                                    SECPKG_ATTR_ISSUER_LIST_EX,
                                    (PVOID)&IssuerListInfo);
    if(Status != SEC_E_OK)
    {
        printf("Error 0x%x querying issuer list info\n", Status);
        return;
    }

    //
    // Перечисление сертификатов клиента.
    //

    ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));

    FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
    FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
    FindByIssuerPara.dwKeySpec = 0;
    FindByIssuerPara.cIssuer   = IssuerListInfo.cIssuers;
    FindByIssuerPara.rgIssuer  = IssuerListInfo.aIssuers;

    pChainContext = NULL;

    for(;;)
    {
        // Поиск цепочки сертификатов.
        pChainContext = CertFindChainInStore(hMyCertStore,
                                             X509_ASN_ENCODING,
                                             0,
                                             CERT_CHAIN_FIND_BY_ISSUER,
                                             &FindByIssuerPara,
                                             pChainContext);
        if(pChainContext == NULL)
        {
            printf("Error 0x%x finding cert chain\n", GetLastError());
            break;
        }
        printf("\ncertificate chain found\n");

        // Получение указателя на контекс сертифика-листа.
        pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;

        // Создание schannel мандата.
        SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
        SchannelCred.cCreds = 1;
        SchannelCred.paCred = &pCertContext;

        Status = g_pSSPI->AcquireCredentialsHandleA(
                            NULL,                   // Имя администратора
                            UNISP_NAME_A,           // Имя пакета
                            SECPKG_CRED_OUTBOUND,   // Флаг, определяющий использование
                            NULL,                   // Указатель на идентификатор пароля
                            &SchannelCred,          // Данные пакета
                            NULL,                   // Указатель на функицю GetKey()
                            NULL,                   // Значения, передаваемые функции GetKey()
                            &hCreds,                // (out) Даскриптор мандата
                            &tsExpiry);             // (out) Период актуальности (необязательно)
        if(Status != SEC_E_OK)
        {
            printf("**** Error 0x%x returned by AcquireCredentialsHandle\n", Status);
            continue;
        }
        printf("\nnew schannel credential created\n");

        // Уничтожение старых мандатов.
        g_pSSPI->FreeCredentialsHandle(phCreds);

        *phCreds = hCreds;

        break;
    }
} // Конец GetNewClientCredentials

    Требования:

    Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).
    Solaris: 9 Update 4 или выше.
    FreeBSD: FreeBSD 5.2 или выше
    Linux: RedHat 7.3, RedHad 9.0.
    Файл описания: Прототип описан в файле sspi.h.


Список требований

Глобальный EnumerateSecurityPackages (PULONG pcPackages, PSecPkgInfo *ppPackageInfo)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный InitSecurityInterface (void)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный QuerySecurityPackageInfo (SEC_CHAR *pszPackageName, PSecPkgInfo *ppPackageInfo)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный AcquireCredentialsHandle (SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse, PLUID pvLogonID, PVOID pAuthData, PVOID pGetKeyFn, PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный FreeCredentialsHandle (PCredHandle phCredential)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный QueryCredentialsAttributes (PCredHandle phCredential, unsigned long ulAttribute, void SEC_FAR *pBuffer)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный AcceptSecurityContext (PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный ApplyControlToken (PCtxtHandle phContext, PSecBufferDesc pInput)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный DeleteSecurityContext (PCtxtHandle phContext)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный InitializeSecurityContext (PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName, ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный QueryContextAttributes (PCtxtHandle phContext, ULONG ulAttribute, PVOID pBuffer)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный QuerySecurityContextToken (PCtxtHandle phContext, HANDLE *phToken)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный DecryptMessage (PCtxtHandle phContext, PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный EncryptMessage (PCtxtHandle phContext, ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Глобальный FreeContextBuffer (PVOID pvContextBuffer)

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Страница Модуль сетевой аутентификации КриптоПро TLS

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    • Страница Пример создания соединения сервера с web клиентом

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Страница Пример создания соединения клиента с web сервером

  • Windows NT/2000/XP/2003: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Windows 95/98/ME: CSP 3.0 не поддерживает (см. КриптоПро CSP 2.0 World Wide Web link ).

    Solaris: 9 Update 4 или выше.

    FreeBSD: FreeBSD 5.2 или выше

    Linux: RedHat 7.3, RedHad 9.0.

    Файл описания: Прототип описан в файле sspi.h.

    • Крипто-Про SSPI Версия: 3.0 Сборка 3293


Требования

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

Формат 1

Требования
Клиент: Требования содержатся в "ВерсииОперационнойСистемы" .
Сервер: Требования содержатся в "ВерсииОперационнойСистемы" .
Дополнительное программное обеспечение: Требования Дополнительного программного обеспечения в "ВерсииОперационнойСистемы" .
Заголовок: Элемент описан в файле "ЗаголовочныйФайл" ; необходимо подключить файл "ВключаемыйЗаголовочныйФайл" .
Библиотека: Необходимо использовать библиотеку "БиблиотечныйФайл" .
Unicode: Элемент реализован в Unicode и ANSI версиях.

Формат 2

Требования
Windows NT/2000/XP: Требования содержатся в "ВерсииОперационнойСистемы" .
Windows 95/98/Me: Требования содержатся в "ВерсииОперационнойСистемы" .
Дополнительное программное обеспечение: Требования Дополнительного программного обеспечения для "ВерсииОперационнойСистемы" .
Заголовок: Элемент описан в файле "ЗаголовочныйФайл" ; необходимо подключить файл "ВключаемыйЗаголовочныйФайл" .
Библиотека: Необходимо использовать библиотеку "БиблиотечныйФайл" .
Unicode: Элемент реализован в Unicode и ANSI версиях.

В строке Клиент приводится список тех клиентских платформ, которые имеют встроенную поддержку данного элемента API. Например, Windows XP, Windows 2000 Professional, Windows NT Workstation, Windows Me, Windows 98 и Windows 95 - все они являются клиентскими платформами.

В строке Сервер указывается список серверных платформ, имеющих встроенную поддержку данного элемента API. Например, Windows Server 2003, Windows 2000 Server, и Windows NT Server - все они являются серверными платформами. Для каждой платформы возможны различные варианты SKU. Если элемент поддерживается только некоторыми из них, это указывается отдельно. В противном случае указываются все SKU. Например, Windows 2000 Server поддерживается платформами Windows 2000 Server, Windows 2000 Advanced Server и Windows 2000 Datacenter Server.

В строке Windows NT/2000/XP указано, имеется ли встроенная поддержка данного элемента в операционных системах Windows Server 2003, Windows XP, Windows 2000 или Windows NT, все из которых написаны на основе кода Windows NT. Если элемент на этих платформах не поддерживается, в данной строке будет указано: "Не поддерживается".

В строке Windows 95/98/Me указывается, имеется ли встроенная поддержка данного элемента в Windows 95, Windows 98 или Windows Me. Если элемент на этих платформах не поддерживается, в данной строке будет указано "Не поддерживается.", либо эта строка будет опущена.

В строке Дополнительное программное обеспечение указываются дополнительные требования для поддержки данного элемента API, не имеющие встроенной поддержки в той или иной версии Windows. Для тех элементов, которые имеют только встроенную поддержку, эта строка опускается.

В строке Заголовок приводится имя заголовочного файла, в котором определен данный элемент API. Это может быть .h-, .idl- или .odl-файл. В этой строке может также указываться, что в пользовательском коде должен использоваться другой заголовочный файл (файл, включающий тот, в котором описан данный элемет API, а также другие необходимые заголовочные файлы и определения). Если заголовочный файл определяется пользователем, или если он вообще не требуется, эта строка опускается.

В строке Библиотека указывается имя библиотечного файла, вызываемого при обращениях к функции или ее элементам. Это может быть .lib-, .dll- или .tlb файл. Если библиотечный файл определяется пользователем, или если он вообще не требуется, эта строка опускается.

В строке Unicode указывается, существует ли элемент как в Unicode (W) версии, так и в ANSI (A) версии. В противном случае, эта строка опускается.

Пример

Ниже описан пример блока требований.

Windows NT/2000/XP: Требования для Windows NT 4.0 и выше.
Windows 95/98/Me: Требования для Windows 98 и выше.
Дополнительное программное обеспечение: Требования IE 4.01 или выше для Windows 95.
Заголовок: Элемент описан в файле Winuser.h; include Windows.h.
Библиотека: Необходимо использовать библиотеку User32.lib.
Unicode: Элемент реализован в Unicode и ANSI версиях.

Функция, к которой относятся эти требования, имеет встроенную поддержку в операционных системах Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, Windows Me и Windows 98. Для вызова этой функции в системе Windows 95, необходимо установить Internet Explorer 4.01 или выше. Для компиляции кода, в котором используется эта функция, в него должен быть включен заголовочный файл Windows.h (хотя для того, чтобы просмотреть объявление функции необходимо обратиться к файлу Winuser.h). Для линковщика необходимо указать библиотеку User32.lib.