КриптоПро CSP 4.0

127018, Москва, ул. Сущёвский вал, д. 18
Телефон: +7 (495) 995 4820
Факс: +7 (495) 984 0790
http://www.CryptoPro.ru
E-mail: info@CryptoPro.ru

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

КриптоПро CSP

Версия 4.0

Руководство программиста
Часть 2
Security Support Provider Interface (SSPI)

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


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

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

СКЗИ КриптоПро CSP (SSP) встраивается в системы Microsoft в качестве дополнения к стандартному модулю Schannel, реализующего протокол TLS/SSL (RFC 2246) на иностранных алгоритмах.

КриптоПро CSP (SSP)

Основной чертой, отличающей КриптоПро CSP (SSP) от остальных разработок, является то что, используя российские криптографические алгоритмы, КриптоПро CSP (SSP) разработано в соответствии с криптографическим интерфейсом корпорации Microsoft - Security Service Provider Interface (SSPI).

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

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

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

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

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

  • Веб-обозреватели - Microsoft Internet Explorer, Google Chrome (Windows), Apple Safari (Windows)
  • Веб-сервер - Microsoft Internet Information Server
  • Firewall - Microsoft Internet Security and Acceleration Server
  • Электронная почта - Microsoft Outlook
  • Электронная почта - Microsoft Outlook Express

См. в этой главе | Важные внешние ссылки
Работа с пакетами безопасности Windows |
Функциональность | Конференция Крипто-Про World Wide Web link
Функции КриптоПро CSP (SSP) | Использование CryptoAPI в MSDN World Wide Web link
Примеры использования функций КриптоПро CSP (SSP) |

КриптоПро SSPI Версия: 4.0

Обратная связь для этой статьи

(c) ООО КРИПТО-ПРО. Все права защищены.


Функциональность

Основные функции КриптоПро CSP (SSP)

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

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

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

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

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

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.


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

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

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

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

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

Встраивание СКЗИ КриптоПро CSP (SSP)

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

Примеры встраивания имеются в (файлы 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.

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

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

Заключение

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


Функции КриптоПро CSP (SSP)

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

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


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

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

Функции | Описание
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.

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

FreeContextBuffer() ,SecPkgInfo World Wide Web link


InitSecurityInterface

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

PSecurityFunctionTable InitSecurityInterface(
  void
);

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

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

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

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.

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

FreeContextBuffer() ,SecPkgInfo World Wide Web link


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

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

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


AcquireCredentialsHandle

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

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] Название пакета безопасности. Используйте UNISP_NAME.
fCredentialUse
[in] Флаг - индикатор использования удостоверения. SECPKG_CRED_INBOUND - для обработки входящих сообщений (сервер), SECPKG_CRED_OUTBOUND - для подготовки исходящих сообщений (клиент).
pvLogonID
Не используется, должен быть NULL.
pAuthData
Данные для создания удостоверения, указатель на структуру SCHANNEL_CRED.
pGetKeyFn
Не используется, должен быть NULL.
pvGetKeyArgument
Не используется, должен быть NULL.
phCredential
[out] Дескриптор удостоверения.
ptsExpiry
[out] Указатель на структуру TimeStamp, содержащую срок действия удостоверения в локальном времени.

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

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

FreeCredentialsHandle()


FreeCredentialsHandle

Функция FreeCredentialsHandle() используется для освобождения удостоверения.

SECURITY_STATUS FreeCredentialsHandle(
  PCredHandle phCredential
);

Аргументы

phCredential
[in] Дескриптор удостоверения.

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

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

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 | Параметр phCredential содержит недействительный дескриптор удостоверения.
SEC_E_UNSUPPORTED_FUNCTION | Параметр ulAttribute содержит неподдерживаемый атрибут.

Примечания

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

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

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


SetCredentialsAttributes

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

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

Аргументы

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

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

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

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

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

QueryCredentialsAttributes()


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

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

Функции | Описание
AcceptSecurityContext | Функция AcceptSecurityContext() создает серверный контекст безопасности, используемый для защищенного обмена между клиентом и сервером.
ApplyControlToken | Функция ApplyControlToken() используется для управления контекстом соединения.
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 после первого вызова к этой функции.
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 | Взаимная аутентификация
Может оказаться так, что сервер поддерживает не все из запрошенных параметров. Дальнейшие детали смотрите в описании параметра pfContextAttr
Reserved1
[in] Зарезервирован. Должен быть 0.
TargetDataRep
[in] Параметр зарезервирован для будущего использования и должен быть 0.
pInput
[in] указатель на структуру SecBufferDesc World Wide Web link. При первом вызове может содержать структуру SecBuffer World Wide Web link типа SECBUFFER_APPLICATION_PROTOCOLS с протоколами уровня приложений, используемых для согласования Application-Layer Protocol Negotiation Protocol Negotiation; данные в буфере должны содержать упакованную структуру SEC_APPLICATION_PROTOCOLS. При последующих вызовах pInput должен состоять из двух структур SecBuffer World Wide Web link. Первый буфер должен иметь тип SECBUFFER_TOKEN, и содержать пакет, полученный от клиента. Второй буфер должен иметь тип SECBUFFER_EMPTY. В него в случае необходимости будут помещены оставшиеся необработанные входные данные с типом SECBUFFER_EXTRA, или, eсли полученное сообщение неполно (SEC_E_INCOMPLETE_MESSAGE), во втором буфере типа SECBUFFER_MISSING будет возвращен ожидаемый размер входных данных.
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. На выходе в этот буфер будет помещен пакет, который нужно переслать серверу. При наличии флага ASC_REQ_ALLOCATE_MEMORY этот буфер будет выделен SSPI и должен быть освобожден с помощью функции FreeContextBuffer.
pfContextAttr
[out] Указатель на ULONG, куда будут записаны флаги, описывающие свойства устанавливаемого контекста. Список возможных значений приведен в описании параметра fContextReq. pfContextAttr получает тот же набор флагов, с заменой префикса ASC_REQ на ASC_RET.
Warning Атрибуты безопасности не следует проверять до последнего вызова функции, возвращающего SEC_E_OK. Остальные атрибуты, такие как ASC_RET_ALLOCATED_MEMORY можно использовать после первого же вызова.
ptsExpiry
[out] Опционален. Указатель на структуру TimeStamp World Wide Web link. Время жизни контекста в UTC.

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

При успешном завершении функция возвращает одно из следующих значений:
Коды возврата | Описание
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_NO_CREDENTIALS | Предоставленное удостоверение недостоверно.
SEC_E_UNSUPPORTED_FUNCTION | Параметр fContextReq содержит неподдерживаемый набор флагов.
SEC_E_INSUFFICIENT_MEMORY | Один из буферов оказался слишком мал. См. заметки.

Примечания

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

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() используется для управления контекстом соединения.

SECURITY_STATUS ApplyControlToken(
  PCtxtHandle phContext,
  PSecBufferDesc pInput
);

Аргументы

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

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

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

Примечания

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

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 содержит недействительный дескриптор контекста.

Примечания

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.


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_APPLICATION_PROTOCOLS с протоколами уровня приложений, используемых для согласования Application-Layer Protocol Negotiation Protocol Negotiation; данные в буфере должны содержать упакованную структуру SEC_APPLICATION_PROTOCOLS. При последующих вызовах pInput должен состоять из двух структур SecBuffer World Wide Web link. Первый буфер должен иметь тип SECBUFFER_TOKEN, и содержать пакет, полученный от сервера. Второй буфер должен иметь тип SECBUFFER_EMPTY. В него в случае необходимости будут помещены оставшиеся необработанные входные данные с типом SECBUFFER_EXTRA, или, eсли полученное сообщение неполно (SEC_E_INCOMPLETE_MESSAGE), во втором буфере типа SECBUFFER_MISSING будет возвращен ожидаемый размер входных данных.
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 этот буфер будет выделен SSPI и должен быть освобожден с помощью функции FreeContextBuffer.
pfContextAttr
[out] Указатель на ULONG, куда будут записаны флаги, описывающие свойства устанавливаемого контекста. Список возможных значений приведен в описании параметра fContextReq. pfContextAttr получает тот же набор флагов, с заменой префикса ISC_REQ на ISC_RET.
Warning Атрибуты безопасности не следует проверять до последнего вызова функции, возвращающего SEC_E_OK. Остальные атрибуты, такие как ISC_RET_ALLOCATED_MEMORY можно использовать после первого же вызова.
ptsExpiry
[out] Опционален. Указатель на структуру TimeStamp. При наличии сертификата клиента, этот параметр получает время истечения этого сертификата. Иначе возвращается максимальное возможное значение времени. Время возвращается в UTC.

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

При успешном завершении функция возвращает одно из следующих значений:
Коды возврата | Описание
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.

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

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_CIPHER_INFO | Информация об используемых сюитах и криптографических алгоритмах. | SecPkgContext_CipherInfo
SECPKG_ATTR_ISSUER_LIST_EX | Список доверенных центров сертификации. | SecPkgContext_IssuerListInfoEx
SECPKG_ATTR_LIFESPAN | Время истечения соединения. Время возвращается в UTC. | SecPkgContext_Lifespan
SECPKG_ATTR_NAMES | Subject из сертификата удаленной стороны. | SecPkgContext_Names
SECPKG_ATTR_REMOTE_CERT_CONTEXT | Сертификат удаленной стороны. | CERT_CONTEXT. См. заметки.
SECPKG_ATTR_STREAM_SIZES | Размеры буферов для заголовка, блока данных и трейлера при шифровании. | SecPkgContext_StreamSizes
SECPKG_ATTR_APPLICATION_PROTOCOL | Согласованный протокол уровня приложения | SecPkgContext_ApplicationProtocol

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

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

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

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

Примечания

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

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

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.


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.

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.


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

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

Функции | Описание
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 | Данные во входном буфере неполны. Приложение должно прочитать остальные данные, и повторить вызов. При этом буфер с данными (SECBUFFER_DATA) и первый пустой буфер (SECBUFFER_EMPTY) будут содержать длину недостающих данных и иметь тип SECBUFFER_EMPTY.
SEC_I_RENEGOTIATE | Другая сторона запросила renegotiate. См. заметки.
SEC_I_CONTEXT_EXPIRED | Другая сторона закрыла соединение. См. заметки.

Примечания

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

  • Вызвать ApplyControlToken с параметром SCHANNEL_SHUTDOWN.
  • После получения SEC_E_OK вызвать InitializeSecurityContext или AcceptSecurityContext c пустыми входными буферами.
  • Отправить полученный вывод.
  • Удалить контекст вызовом DeleteSecurityContext.

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

    Требования:

    AIX: 5.3 или выше.
    FreeBSD: 7 или выше.
    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
    Solaris: 10 или выше.
    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    См. также

    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

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

    AIX: 5.3 или выше.
    FreeBSD: 7 или выше.
    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
    Solaris: 10 или выше.
    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    См. также

    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.

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

См. также

InitializeSecurityContext()


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

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

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


CompleteAuthToken

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

SECURITY_STATUS CompleteAuthToken(
  PCtxtHandle phContext,
  PSecBufferDesc pToken
);

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

Всегда возвращает SEC_E_INTERNAL_ERROR.


ImpersonateSecurityContext

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

SECURITY_STATUS ImpersonateSecurityContext(
  PCtxtHandle phContext
);

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

Всегда возвращает SEC_E_INTERNAL_ERROR.


MakeSignature

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

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

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

Всегда возвращает SEC_E_INTERNAL_ERROR.

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.


RevertSecurityContext

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

SECURITY_STATUS RevertSecurityContext(
  PCtxtHandle phContext
);

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

Всегда возвращает SEC_E_INTERNAL_ERROR.


CP_VerifySignature

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

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

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

Всегда возвращает SEC_E_INTERNAL_ERROR.

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.


Структуры данных и функции работы с TLS в ядре и экспорта сессий

Функции и структуры данных, позволяющие пробрасывать контекст сессии в ядро и экспортировать сессии на другие узлы кластера

  • Структуры данных и функции уровня пользователя
  • Структуры данных и функции TLS уровня ядра


Структуры данных и функции уровня пользователя

Структуры | Описание
utls_gost_in_st | Структура utls_gost_in, в которой передается данные для создания utls_gost_handle.
utlsdb_token | Структура ключевой пары экспорта.
_CPUTLS_vtbl_ | Структура, содержащая таблицы работы с ассоциациями ключей и функций SSPI.
utls_gost_out_st | Структура utls_gost_out, в которой возвращаются данные для создания utls_gost_handle.
Функции | Описание
ExportSecurityContext | Функция CPExportSecurityContext() сохраняет контекст в буфере. В зависимости от поля fFlags либо сохраняет контекст для использования в ядре ОС, либо сохраняет сессию для использования на другом узле кластера.
ImportSecurityContext | Функция CPImportSecurityContext() импортирует контекст или сессию из буфера.
cputls_init_gost | Функция cputls_init_gost() используется для инициализации библиотеки TLS.
cputls_shutdown_gost | Функция cputls_shutdown_gost() завершает работу библиотеки.
Макросы | Описание
CPUTLS_CONTEXT_EXPORT_TO_KERNEL | экспорт сессионных ключей для модуля ядра
CPUTLS_ATTR_CREDENTIAL_HANDLE | вернуть хендл на созданные в процессе импорта Credentials


CPUTLS_CONTEXT_EXPORT_TO_KERNEL

экспорт сессионных ключей для модуля ядра
#define CPUTLS_CONTEXT_EXPORT_TO_KERNEL (0x1000)


CPUTLS_ATTR_CREDENTIAL_HANDLE

вернуть хендл на созданные в процессе импорта Credentials
#define CPUTLS_ATTR_CREDENTIAL_HANDLE (0x1001)


ExportSecurityContext

Функция CPExportSecurityContext() сохраняет контекст в буфере. В зависимости от поля fFlags либо сохраняет контекст для использования в ядре ОС, либо сохраняет сессию для использования на другом узле кластера.

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

Примечания

Для Windows - систем функция является заглушкой и возвращает SEC_E_UNSUPPORTED_FUNCTION

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

Аргументы

phContext
[in] контекст TLS
fFlags
Если в поле fFlags установлен флаг CPUTLS_CONTEXT_EXPORT_TO_KERNEL, то происходит экспорт контекста в ядро, в противном случае происходит экспорт сессии
pPackedContext
[in/out] указатель на структуру SecBuffer World Wide Web link . Если поле pvBuffer этой структуры равен NULL, то после вызова в поле cbBuffer будет возвращен размер буфера, который надо аллоцировать. В противном случае при успешном завершении в буфер будет записаны данные
pToken
[in] туда передается информация (структура utlsdb_token ) о закрытом ключе пользовательского процесса и открытом ключе получателя (модуля ядра или узла кластера), необходимая для создания ключа обмена

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

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

В случае успеха возвращает STATUS_SUCCESS
Коды возврата | Описание
STATUS_NO_MEMORY | передаваемый буфер слишком мал
STATUS_INTERNAL_ERROR | произошла ошибка экспорта


ImportSecurityContext

Функция CPImportSecurityContext() импортирует контекст или сессию из буфера.

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

Примечания

Для Windows - систем функция является заглушкой и возвращает SEC_E_UNSUPPORTED_FUNCTION

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

Если буфер содержит экспортированный контекст уровня ядра, то ищется контекст пользователя, из которого он был экспортирован, данный контекст обновляется и возвращается.

Если буфер содержит эспортированную сессию, то в кэше сессий ищется указанная сессия, и в отсутствие таковой она импортируется из буфера. Далее на этой сессии создается контекст и возвращается пользователю.

Аргументы

pszPackage
[in] не используется и устанавливается в NULL
pPackedContext
[in] указатель на структуру SecBuffer World Wide Web link , в которой записана эспортированная сессия или экспортированный контекст.
pToken
[in] туда передается информация (структура utlsdb_token ) о закрытом ключе пользовательского процесса и открытом ключе получателя (модуля ядра или узла кластера), необходимая для создания ключа обмена
phContext
[out] контекст TLS, полученный в результате выполнения

Требования:

AIX: 5.3 или выше.
FreeBSD: 7 или выше.
Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
Solaris: 10 или выше.
Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

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

В случае успеха возвращает STATUS_SUCCESS
Коды возврата | Описание
STATUS_INTERNAL_ERROR | произошла ошибка импорта


cputls_init_gost

Функция cputls_init_gost() используется для инициализации библиотеки TLS.

DWORD cputls_init_gost(
  utls_gost_handle h,
  const utls_gost_in * in,
  unsigned uFlags,
  utls_gost_out * out
);

Аргументы

h
[in/out] - указатель на область памяти под таблицы функций библиотеки.
in
[in] - указатель на структуру utls_gost_in_st в которой указывается размер выделенной памяти
uFlags
[in] - не используется, заполняется нулем
out
[out] - указатель на структуру utls_gost_out_st в которой указывается размер памяти, которую необходимо выделить

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

возвращает статус выполнения операции
Коды возврата | Описание
STATUS_SUCCESS | Успешное завершение. Если параметр h равен NULL в поле out возвращается размер памяти, которую надо выделить под структуру
STATUS_INTERNAL_ERROR | параметры функции заданы неверно
STATUS_NO_MEMORY | выделенной памяти не хватает под структуру


cputls_shutdown_gost

Функция cputls_shutdown_gost() завершает работу библиотеки.

DWORD cputls_shutdown_gost(
  utls_gost_handle h,
  unsigned uFlags
);

Примечания

Память, зарезервированную в utls_gost_handle освобождает пользователь

Аргументы

h
указатель на освобождаемую структуру
uFlags
не используется, должно быть равно 0

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

STATUS_SUCCESS


utls_gost_in_st

Структура utls_gost_in, в которой передается данные для создания utls_gost_handle.
typedef struct utls_gost_in_st {
  unsigned allocatedMem;
  void * reserved[7];
} utls_gost_in;

Члены

allocatedMem
Размер выделенной под utls_gost_handle памяти (не менее requiredMem в utls_gost_out_st).
reserved
Зарезервировано, не используется, заполняется нулями.


utlsdb_token

Структура ключевой пары экспорта.
struct utlsdb_token {
  PRIVKEY Priv;
  PUBKEY_2012 Pub;
};

Члены

Priv
Закрытый ключ отправителя.
Pub
Открытый ключ получателя.


_CPUTLS_vtbl_

Структура, содержащая таблицы работы с ассоциациями ключей и функций SSPI.
typedef struct _CPUTLS_vtbl_ {
  GEN_SADB tlsdb;
  PSecurityFunctionTable pSSPI;
} CPUTLS_VTBL, *utls_gost_handle;

Члены

tlsdb
Таблица функций SADB.
pSSPI
Таблица функций SSPI.


utls_gost_out_st

Структура utls_gost_out, в которой возвращаются данные для создания utls_gost_handle.
typedef struct utls_gost_out_st {
  unsigned requiredMem;
  void * reserved[7];
} utls_gost_out;

Члены

requiredMem
Необходимый для utls_gost_handle размер памяти.
reserved
Зарезервировано, не используется, заполняется нулями.


Структуры данных и функции TLS уровня ядра

Структуры | Описание
ktls_gost_in_st |
ktls_gost_out_st |
CPKTLS_HANDLE |
_CPKTLS_vtbl_ | Структура, содержащая указатели на функции TLS в ядре и таблицу функций SADB.
Функции | Описание
cpktls_init_gost | Функция cpktls_init_gost() предназначена для инициализации библиотеки TLS уровня ядра.
cpktls_shutdown_gost | cpktls_shutdown_gosti() - функция завершения работы библиотеки TLS.
ImportContext | Функция ImportContext() считывает экспортированный контекст из буфера (аналог ImportSecurityContext() уровня пользователя).
ExportContext | Функция ExportContext() сохраняет контекст в буфере (аналог ExportSecurityContext() уровня пользователя).
Seal | Функция Seal() зашифровывает сообщение с использованием сессионного ключа, выработанного совместно с удаленной стороной, и вычисляет имитовставку.
UnSeal | Функция UnSeal() расшифровывает сообщение, проверяет его целостность и уведомляет о получении запроса на переустановку связи или разрыв соединения.
QueryAttributes | Функция QueryAttributes() возвращает информацию о свойствах данного контекста (аналог QueryContextAttributes() уровня пользователя).
CompleteToken | Функция CompleteToken() в настоящей реализации не используется.
DeleteContext |
Определения типов | Описание
KTLSLogDecl |
KTLSSetLogLvlDecl |
Макросы | Описание
CPKTLS_CONTEXT_EXPORT_TO_USER |

Примечания

На данный момент модуль ядра реализован для Linux-систем


CPKTLS_CONTEXT_EXPORT_TO_USER

экспорт сессионных ключей

#define CPKTLS_CONTEXT_EXPORT_TO_USER (0x10000)


KTLSLogDecl

Прототип функции протоколирования

typedef void CAPI_EXTC KTLSLogDecl(void *pUfnAgr, log_severity_t severity, unsigned uFlags, const char *fmt,...);


KTLSSetLogLvlDecl

Прототип функции установления уровня протоколирования

typedef void CAPI_EXTC KTLSSetLogLvlDecl(void *pUfnAgr, log_severity_t severity, unsigned uFlags);


cpktls_init_gost

Функция cpktls_init_gost() предназначена для инициализации библиотеки TLS уровня ядра.

DWORD CAPI_EXTC cpktls_init_gost(
  ktls_gost_handle h,
  const ktls_gost_in * in,
  unsigned uFlags,
  ktls_gost_out * out
);

Аргументы

h
[in/out]- указатель на область памяти, где будут размещены указатели на функции TLS уровня ядра и таблица указателей на функции SADB
in
[in] - структура, в которой передаются параметры для инициализации таблицы (размер памяти, выделенный под нее)
uFlags
[in] - в настоящей версии не используется и должен быть установлен в 0
out
[out] - структура, в которой возвращается размер памяти, необходимый для резервирования структуры

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

в случае успеха возвращается STATUS_SUCCESS
Коды возврата | Описание
STATUS_SUCCESS | операция прошла успешно. Если в качестве параметра h передается NULL, в поле out возвращается количество памяти, которую необходимо зарезервировать.
STATUS_NO_MEMORY | выделено недостаточно памяти под структуру
STATUS_INTERNAL_ERROR | заданы неверные параметры вызова функции


cpktls_shutdown_gost

cpktls_shutdown_gosti() - функция завершения работы библиотеки TLS.

DWORD CAPI_EXTC cpktls_shutdown_gost(
  ktls_gost_handle h,
  unsigned uFlags
);

Аргументы

h
[in/out] - указатель на хендл TLS
uFlags
- не используются, должен быть равен 0

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

STATUS_SUCCESS


ImportContext

Функция ImportContext() считывает экспортированный контекст из буфера (аналог ImportSecurityContext() уровня пользователя).

DWORD CAPI_EXTC ImportContext(
  ktls_gost_handle h,
  PSecBuffer pPackedContext,
  PRIVKEY * hPriv,
  PUBKEY_2012 * hPub,
  unsigned uFlags,
  CPKTLS_HANDLE * phCtx
);

Аргументы

h
[in/out] - хендл TLS ядра
pPackedContext
[in] - эспортированный из пользовательского процесса контекст
hPriv
[in] - указатель на закрытый ключ ядра
hPub
[in] - указатель на открытый ключ пользовательского процесса
uFlags
[in] - не используется, устанавливается в 0
phCtx
[out] - указатель на создаваемый контекст TLS уровня ядра

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

SEC_E_OK, если операция прошла успешно


ExportContext

Функция ExportContext() сохраняет контекст в буфере (аналог ExportSecurityContext() уровня пользователя).

DWORD CAPI_EXTC ExportContext(
  ktls_gost_handle h,
  CPKTLS_HANDLE hCtx,
  PRIVKEY * Priv,
  PUBKEY_2012 * Pub,
  unsigned uFlags,
  PSecBuffer pPackedContext
);

Аргументы

h
[in/out] - хендл TLS ядра
hCtx
[in] - экспортируемый контекст
Priv
[in] - указатель на закрытый ключ ядра
Pub
[in] - указатель на открытый ключ пользовательского процесса
uFlags
[in] - не используется, устанавливается в 0
pPackedContext
[in/out] - экспортированный контекст уровня ядра

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

SEC_E_OK если операция прошла успешно


Seal

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

DWORD CAPI_EXTC Seal(
  ktls_gost_handle h,
  CPKTLS_HANDLE hCtx,
  IN ULONG fQOP,
  IN OUT PSecBufferDesc Message,
  IN ULONG MessageSeqNo
);

Примечания

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

Аргументы

h
[in/out] хендл TLS уровня ядра
hCtx
[in] Дескриптор контекста соединения уровня ядра, установленного с получателем сообщения.
fQOP
[in] Параметр зарезервирован для будущего использования и должен быть 0.
Message
[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 | Параметр hCtx содержит недействительный дескриптор контекста.
SEC_E_INVALID_TOKEN | Параметр сообщение содержит неправильные входные данные.
SEC_E_CONTEXT_EXPIRED | Другая сторона закрыла соединение. См. заметки.

Примечания

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

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

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

    • См. также

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


UnSeal

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

DWORD CAPI_EXTC UnSeal(
  ktls_gost_handle h,
  CPKTLS_HANDLE hCtx,
  IN OUT PSecBufferDesc Message,
  IN ULONG MessageSeqNo,
  OUT PULONG pfQOP
);

Примечания

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

Аргументы

h
[in/out] хендл TLS уровня ядра
hCtx
[in] Дескриптор контекста соединения уровня ядра, установленного с получателем сообщения.
Message
[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 | Данные во входном буфере неполны. Приложение должно прочитать остальные данные, и повторить вызов. При этом буфер с данными (SECBUFFER_DATA) и первый пустой буфер (SECBUFFER_EMPTY) будут содержать длину недостающих данных и иметь тип SECBUFFER_EMPTY.
SEC_I_RENEGOTIATE | Другая сторона запросила renegotiate. См. заметки.
SEC_I_CONTEXT_EXPIRED | Другая сторона закрыла соединение.

Примечания

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

См. также

ImportContext() ,ExportContext() ,DeleteContext() ,Seal() ,SecBuffer World Wide Web link ,SecBufferDesc World Wide Web link


QueryAttributes

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

DWORD CAPI_EXTC QueryAttributes(
  ktls_gost_handle h,
  CPKTLS_HANDLE hCtx,
  IN ULONG Attribute,
  IN OUT PVOID Buffer
);

Аргументы

h
[in] - хендл TLS уровня ядра
hCtx
[in] - контекст TLS уровня ядра
Attribute
[in] Указывает, какой атрибут запрашивается. Допустимые атрибуты перечислены в следующей таблице:
Значение | Возвращаемая информация | Тип структуры pBuffer
SECPKG_ATTR_CONNECTION_INFO | Протокол и используемые криптографические алгоритмы. | SecPkgContext_ConnectionInfo
SECPKG_ATTR_STREAM_SIZES | Размеры буферов для заголовка, блока данных и трейлера при шифровании. | SecPkgContext_StreamSizes

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

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

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

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


CompleteToken

Функция CompleteToken() в настоящей реализации не используется.

DWORD CAPI_EXTC CompleteToken(
  ktls_gost_handle h,
  CPKTLS_HANDLE hCtx,
  IN PSecBufferDesc Token
);

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

STATUS_INTERNAL_ERROR


DeleteContext

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

DWORD CAPI_EXTC DeleteContext(
  ktls_gost_handle h,
  unsigned uFlags,
  CPKTLS_HANDLE hCtx
);

Аргументы

h
[in] - хендл TLS уровня ядра
uFlags
[in] - не используется, должно быть равно 0
hCtx
[in] - удаляемый контекст TLS уровня ядра

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

STATUS_SUCCESS


ktls_gost_in_st

Структура в которой передается данные для создания ktls_gost_handle

typedef struct ktls_gost_in_st {
  void * pUfnArg;
  KTLSLogDecl * LogUfn;
  KTLSSetLogLvlDecl * SetLogLvlUfn;
  unsigned allocatedMem;
  void * reserved[4];
  LPCPC_CONFIG pConfig;
  HCRYPTMODULE hCSP;
} ktls_gost_in;

Члены

pUfnArg
Пользовательский аргумент функции протоколирования.
LogUfn
Функция протоколирования , может быть 0.
SetLogLvlUfn
Функция управления протоколированием, устанавливается при использовании LogUfn.
allocatedMem
Размер выделенной под ktls_gost_handle памяти (не менее requiredMem в ktls_gost_out_st).
reserved
Зарезервировано, не используется, заполняется нулями.
pConfig
указатель на конфигурацию, с которой был создан провайдер
hCSP
указатель на провайдер, в котором будет работать TLS


ktls_gost_out_st

Структура в которой возвращаются данные для создания ktls_gost_handle

typedef struct ktls_gost_out_st {
  unsigned requiredMem;
  void * reserved[7];
} ktls_gost_out;

Члены

requiredMem
Необходимый для ktls_gost_handle размер памяти.
reserved
Зарезервировано, не используется, заполняется нулями.


CPKTLS_HANDLE

Хендл на контекст уровня ядра

struct CPKTLS_HANDLE {
  int dummy;
};

Члены

dummy


_CPKTLS_vtbl_

Структура, содержащая указатели на функции TLS в ядре и таблицу функций SADB.
typedef struct _CPKTLS_vtbl_ {
  ImportContext_t * ImportContext;
  ExportContext_t * ExportContext;
  Seal_t * Seal;
  UnSeal_t * UnSeal;
  QueryAttributes_t * QueryAttributes;
  CompleteToken_t * CompleteToken;
  DeleteContext_t * DeleteContext;
  GEN_SADB tlsdb;
} CPKTLS_VTBL, *ktls_gost_handle;

Члены

ImportContext
Указатель на ImportContext
ExportContext
Указатель на ExportContext
Seal
Указатель на Seal
UnSeal
Указатель на UnSeal
QueryAttributes
Указатель на QueryAttributes
CompleteToken
Указатель на CompleteToken
DeleteContext
Указатель на DeleteContext
tlsdb
Таблица функций SADB


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

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

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


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

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

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


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

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

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

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

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

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#ifdef _WIN32
#   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 IS_SOCKET_ERROR(a) (a==SOCKET_ERROR)
#   define INVALID_VALUE INVALID_HANDLE_VALUE
#   define snprintf _snprintf
#else
#   include "CSP_WinDef.h"
#   include "CSP_WinCrypt.h"
#   include "CSP_Sspi.h"
#   include "CSP_SChannel.h"
#   include "reader/support.h"
#   include <sys/types.h>
#   include <sys/socket.h>
# if defined (_AIX) || defined(ANDROID)
#   include <fcntl.h>
# else
#   include <sys/fcntl.h>
# endif
#   include <sys/stat.h>
#   include <fcntl.h>
#   include <unistd.h>
#   include <netinet/in.h>
#   include <errno.h>
#   define INVALID_SOCKET (-1)
#   define INVALID_VALUE (-1)
#   define IS_SOCKET_ERROR(a) (a<0)
    typedef int SOCKET;
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr *LPSOCKADDR;
    static int WSAGetLastError()
    {
      return errno;
    }
#   define LocalFree free
#   define MoveMemory memmove
#   define closesocket close
#   define CloseHandle close
#   define RtlZeroMemory(a, b) memset(a, 0, b)
#   define min(a, b) ((a)<(b)?(a):(b))
#endif

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

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

static HCERTSTORE  hMyCertStore = 0;

static PSecurityFunctionTable g_pSSPI;

static CHAR IoBuffer[IO_BUFFER_SIZE];
static DWORD cbIoBuffer = 0;

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);

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

static 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

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

#ifdef _WIN32
HMODULE g_hSecurity = NULL;
#endif

static BOOL
LoadSecurityLibrary(void)
{
    INIT_SECURITY_INTERFACE         pInitSecurityInterface;
#ifdef _WIN32
    g_hSecurity = LoadLibrary(NT4_DLL_NAME);
    if(g_hSecurity == NULL)
    {
        printf("Error 0x%x loading %s.\n", GetLastError(), NT4_DLL_NAME);
        return FALSE;
    }

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

#else
    pInitSecurityInterface=InitSecurityInterfaceA;
#endif

    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.

#ifdef _WIN32
int _cdecl
#else
int
#endif
main(int argc, char *argv[])
{
#ifdef _WIN32
    WSADATA WsaData;
#endif
    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();
            return 1;
        }

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

        switch(iOption)
        {

        case 'u':
            pszUserName = pszOption;
            break;

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

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

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

    if(WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
    {
        printf("Error %d returned by WSAStartup\n", GetLastError());
        exit(1);
    }
#endif
    //
    // Создание мандатов сервера.
    //

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

    WebServer(&hServerCreds);

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

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

    // Закрытие хранилища сертификатов "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[OBJECT_NAME_LENGTH_MAX];
  DWORD currentDirectoryLength;
#ifdef WIN32
  HANDLE objectHandle = INVALID_VALUE;
#else
  int objectHandle = -1;
#endif
  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;

#if 1
  HANDLE hUserToken = NULL;
  BOOL fImpersonating = FALSE;
#endif

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

  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;

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

#ifdef _WIN32
  currentDirectoryLength = GetCurrentDirectory( OBJECT_NAME_LENGTH_MAX, objectName );
  if ( currentDirectoryLength == 0 )
    {
      printf( "GetCurrentDirectory failed: %ld\n", GetLastError( ) );
      exit(1);
    }
#else
    if(!getcwd(objectName, OBJECT_NAME_LENGTH_MAX))
    {
      printf( "getcwd failed: %ld\n", GetLastError( ) );
      exit(1);
    }
    currentDirectoryLength=strlen(objectName);
#endif

  //
  // Настройка сокета, слушающего порт 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, (LPSOCKADDR) &address, sizeof(address));
  if (IS_SOCKET_ERROR(err)){
    printf("bind failed: %ld\n", GetLastError());
    exit(1);
  }

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

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

  for (;;) {
    PSecBuffer pDataBuffer;
    int filelen;

    fContextInitialized = FALSE;

    objectHandle = INVALID_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 (IS_SOCKET_ERROR(err) || (err == 0))
            {
              printf("recv failed: %ld %ld\n", err, 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;
      }

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

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

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

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

      filelen=fileInfo.nFileSizeLow;
    }
#else
    {
      struct stat buf;
      int objectHandle=open(objectName, O_RDONLY);
      if(objectHandle == -1){
        printf("open(%s) failed: %ld\n", objectName, GetLastError());
        goto cleanup;
      }
      if(fstat(objectHandle, &buf)){
        printf("fstat failed: %ld\n", GetLastError());
        goto cleanup;
      }
      filelen=buf.st_size;
    }
#endif
    ZeroMemory(IoBuffer, Sizes.cbHeader);
    i = snprintf(IoBuffer + Sizes.cbHeader,IO_BUFFER_SIZE,
                "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n",
                filelen);

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

    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 (IS_SOCKET_ERROR(err)){
      printf( "send failed: %ld\n", GetLastError( ) );
      goto cleanup;
    }

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

    for(bytesSent = 0;
        bytesSent < (INT) filelen;
        bytesSent += bytesRead)
      {

#ifdef _WIN32
        if(!ReadFile(objectHandle,
                     IoBuffer + Sizes.cbHeader,
                     IO_BUFFER_SIZE - (Sizes.cbHeader + Sizes.cbTrailer),
                     &bytesRead,
                     NULL))
          {
            printf( "ReadFile failed: %ld\n", GetLastError( ) );
            break;
          }
#else
        if((bytesRead=read(objectHandle,
                           IoBuffer + Sizes.cbHeader,
                           IO_BUFFER_SIZE - (Sizes.cbHeader + Sizes.cbTrailer))==-1)){
          printf( "read failed: %ld\n", GetLastError( ) );
          break;
        }
#endif

        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 (IS_SOCKET_ERROR(err)){
          printf( "send failed: %ld\n", GetLastError( ) );
          break;
        }
      }

  cleanup:

#ifdef _WIN32 // Anyway it's not used.
    if(fImpersonating)
      {
        RevertToSelf();
        fImpersonating = FALSE;
      }

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

    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_VALUE)
      {
        CloseHandle(objectHandle);
      }

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

    // Закрытие сокета.
    if(Socket != INVALID_SOCKET)
      {
        closesocket(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;
    unsigned long        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 (IS_SOCKET_ERROR(err) || err == 0)
                {
                    printf(" recv failed: %ld %d\n", err, 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 );

                if (IS_SOCKET_ERROR(err) || err != OutBuffers[0].cbBuffer)
                {
                    printf(" send failed: %ld %d\n", err, GetLastError() );
                    return FALSE;
                }
                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;
            }
            {
                //The sample of using tsExpiry parametre. Valid only on last call
                //of AcceptSecurityContext
                double hi = tsExpiry.HighPart;
                double lo = tsExpiry.LowPart;
                    // Convert 100-ns interval since January 1, 1601 (UTC)
                    // to 1-sec interval science January 1, 1970, UTC
                time_t clock_1970 = (time_t)(
                        ((ldexp(hi, 32) + lo)*100.e-9)
                        - 11644473600. //SystemTimeToFileTime({.wYear = 1970, .wMonth = 1, .wDay = 1}...
                    );
                    // Convert UTC time_t to local time string
                printf("Security Context of this session valid till %s (local time)\n", ctime(&clock_1970));
            }
            {
                // Sample for usage QueryContextAttributes() & FileTimeToSystemTime()
                FILETIME   ft;
                SYSTEMTIME st;
                unsigned hi;
                unsigned lo;
                SecPkgContext_Lifespan ls;

                // SECPKG_ATTR_LIFESPAN при правильном вызове ошибок вернуть не может
                scRet = g_pSSPI->QueryContextAttributes(phContext,                                      SECPKG_ATTR_LIFESPAN, &ls);
                if(scRet != SEC_E_OK)
                    return FALSE;

                hi = ls.tsStart.HighPart;
                lo = ls.tsStart.LowPart;
                memcpy(&ft, &ls.tsStart, sizeof(ft));
                FileTimeToSystemTime(&ft,&st);
                printf("Connection start {%x, %x}: %d/%d/%d %d:%d:%d.%03d UTC\n",
                            hi, lo,
                            st.wYear, st.wMonth, st.wDay,
                            st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);

                hi = ls.tsExpiry.HighPart;
                lo = ls.tsExpiry.LowPart;
                memcpy(&ft, &ls.tsExpiry, sizeof(ft));
                FileTimeToSystemTime(&ft,&st);
                printf("Connection expiry {%x, %x}: %d/%d/%d %d:%d:%d.%03d UTC\n",
                            hi, lo,
                            st.wYear, st.wMonth, st.wDay,
                            st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
            }
            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';
//            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;
            }

            ObjectName[i] = '\0';

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

            if(strcmp(ObjectName, "/") == 0)
            {
                strncpy(ObjectName,"/Default.Htm",OBJECT_NAME_LENGTH_MAX);
            }
            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;
    unsigned long   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(IS_SOCKET_ERROR(cbData)|| 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;

        snprintf(rgbLine, 100, "%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

    Требования:

    AIX: 5.3 или выше.
    FreeBSD: 7 или выше.
    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
    Solaris: 10 или выше.
    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.


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

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

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

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

//--------------------------------------------------------------------
// В данном примере устанавливается соединение клиента с web сервером
// по защищенному каналу.
// Замечание: под win32 рекомендуется использовать _s аналоги CRT функций.
//--------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#ifdef _WIN32
#   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 IS_SOCKET_ERROR(a) (a==SOCKET_ERROR)
#else
#   include "CSP_WinDef.h"
#   include "CSP_WinCrypt.h"
#   include "CSP_Sspi.h"
#   include "CSP_SChannel.h"
#   include <sys/types.h>
#   if defined (_AIX) || defined (ANDROID)
#       include <fcntl.h>
#   else
#       include <sys/fcntl.h>
#   endif
#   include <sys/stat.h>
#   include <sys/socket.h>
#   include <netinet/in.h>
#   include <arpa/inet.h>
#   include <netdb.h>
#   include <errno.h>
#   include <unistd.h>

#   define INVALID_SOCKET (-1)
#   define IS_SOCKET_ERROR(a) (a<0)
    typedef int SOCKET;
    typedef struct sockaddr_in SOCKADDR_IN;
    typedef struct sockaddr *LPSOCKADDR;
    static int WSAGetLastError()
    {
      return errno;
    }
#   define LocalAlloc(a, b) malloc(b)
#   define LocalFree free
#   define MoveMemory memmove
#   define closesocket close
#endif

#   include "WinCryptEx.h"

#define IO_BUFFER_SIZE  0x8000

#define NT4_DLL_NAME TEXT("Security.dll")

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

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

static HCERTSTORE      hMyCertStore = NULL;
static SCHANNEL_CRED   SchannelCred;

#ifdef _WIN32
static HMODULE g_hSecurity = NULL;
#endif

static 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);

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

//-------------------------------------------------------------
// Функция загрузки библиотеки.
static BOOL
LoadSecurityLibrary(void)
{
    INIT_SECURITY_INTERFACE         pInitSecurityInterface;
#ifdef _WIN32
    g_hSecurity = LoadLibrary(NT4_DLL_NAME);
    if(g_hSecurity == NULL)
    {
        printf("Error 0x%x loading %s.\n", GetLastError(), NT4_DLL_NAME);
        return FALSE;
    }

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

#else
    pInitSecurityInterface=InitSecurityInterfaceA;
#endif

    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

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

static void
UnloadSecurityLibrary(void)
{
#ifdef _WIN32
    FreeLibrary(g_hSecurity);
    g_hSecurity = NULL;
#endif
} //UnloadSecurityLibrary

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

#ifdef _WIN32
int _cdecl
#else
int
#endif
main(int argc, char *argv[])
{
#ifdef _WIN32
    WSADATA WsaData;
#endif
    SOCKET  Socket = INVALID_SOCKET;

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

    SecBuffer  ExtraData;
    SECURITY_STATUS Status;

    PCCERT_CONTEXT pRemoteCertContext = NULL;

    int ret = 1;
    INT i;
    INT iOption;
    PCHAR pszOption;

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

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

    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 1;
        }

        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 1;
        }
    }

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

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

    if(WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
    {
        printf("Error %d returned by WSAStartup\n", GetLastError());
        goto cleanup;
    }
#endif
    //
    // Создание мандатов.
    //

    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))
    {
        LocalFree(ExtraData.pvBuffer); /* pacify CSA */
        printf("Error performing handshake\n");
        goto cleanup;
    }
    LocalFree(ExtraData.pvBuffer); /* pacify CSA */
    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;

    ret = 0;
cleanup:

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

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

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

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

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

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

    UnloadSecurityLibrary();

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

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

    DWORD           cSupportedAlgs = 0;
    ALG_ID          rgbSupportedAlgs[16];

    // Открытие хранилища сертификатов "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;
    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 уже создана его копия.
    //

    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(IS_SOCKET_ERROR(connect(Socket, (struct sockaddr *)&sin, sizeof(sin))))
    {
      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;
    unsigned long   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(IS_SOCKET_ERROR(cbData) || 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;
    unsigned long   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(IS_SOCKET_ERROR(cbData) || 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;
    unsigned long   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(IS_SOCKET_ERROR(cbData))
                {
                    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(IS_SOCKET_ERROR(cbData) || 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)
        {
            DWORD x;
            for (x = 0; x < InBuffer.cBuffers; x++)
                if (InBuffers[x].BufferType == SECBUFFER_MISSING)
                    printf("InBuffers[%u].SECBUFFER_MISSING: %lu bytes.\n", x, InBuffers[x].cbBuffer);
            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;
            }

            //
            // Выход
            //
            {
                // Sample for usage QueryContextAttributes() & native TimeStamp
                SecPkgContext_Lifespan ls;
                double hi, lo;
                time_t clock_1970;

                scRet = g_pSSPI->QueryContextAttributes(phContext,
                                        SECPKG_ATTR_LIFESPAN, &ls);
                hi = ls.tsExpiry.HighPart;
                lo = ls.tsExpiry.LowPart;
                    // Convert 100-ns interval since January 1, 1601 (UTC)
                    // to 1-sec interval science January 1, 1970, UTC
                clock_1970 = (time_t)(
                        ((ldexp(hi, 32) + lo)*100.e-9)
                        - 11644473600. //SystemTimeToFileTime({.wYear = 1970, .wMonth = 1, .wDay = 1}...
                    );
                    // Convert UTC time_t to local time string
                printf("Security Context of this session valid till %s (local time)\n", ctime(&clock_1970));
            }
            {
                // Sample for usage QueryContextAttributes() & FileTimeToSystemTime()
                FILETIME   ft;
                SYSTEMTIME st;
                unsigned hi;
                unsigned lo;
                SecPkgContext_Lifespan ls;

                scRet = g_pSSPI->QueryContextAttributes(phContext,
                                        SECPKG_ATTR_LIFESPAN, &ls);
                hi = ls.tsStart.HighPart;
                lo = ls.tsStart.LowPart;
                memcpy(&ft, &ls.tsStart, sizeof(ft));
                FileTimeToSystemTime(&ft,&st);
                printf("Connection start {%x, %x}: %d/%d/%d %d:%d:%d.%03d UTC\n",
                            hi, lo,
                            st.wYear, st.wMonth, st.wDay,
                            st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);

                hi = ls.tsExpiry.HighPart;
                lo = ls.tsExpiry.LowPart;
                memcpy(&ft, &ls.tsExpiry, sizeof(ft));
                FileTimeToSystemTime(&ft,&st);
                printf("Connection expiry {%x, %x}: %d/%d/%d %d:%d:%d.%03d UTC\n",
                            hi, lo,
                            st.wYear, st.wMonth, st.wDay,
                            st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
            }
            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;

            // Исправляем ошибку Platform SDK!
            // Считаем, что за этим сообщением не может следовать другое
            cbIoBuffer = 0;

            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;// Могут быть такие реализации
    //сервера и клиента что длина буфера будет cbIoBufferLength
    // вычисленная таким образом + 2048 байт см. RFC 2246

    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(IS_SOCKET_ERROR(cbData) || 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(IS_SOCKET_ERROR(cbData))
            {
                printf("**** Error %d reading data from server\n", WSAGetLastError());
                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_EPHEM:
            printf("Key exchange: DH Ephemeral\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

    Требования:

    AIX: 5.3 или выше.
    FreeBSD: 7 или выше.
    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.
    Solaris: 10 или выше.
    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.
    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.


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

Глобальный EnumerateSecurityPackages

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный InitSecurityInterface

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный QuerySecurityPackageInfo

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный AcquireCredentialsHandle

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный FreeCredentialsHandle

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный QueryCredentialsAttributes

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный SetCredentialsAttributes

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный AcceptSecurityContext

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный ApplyControlToken

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный DeleteSecurityContext

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный InitializeSecurityContext

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный QueryContextAttributes

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный QuerySecurityContextToken

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный DecryptMessage

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный EncryptMessage

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный FreeContextBuffer

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный CP_VerifySignature

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный MakeSignature

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный ExportSecurityContext

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Глобальный ImportSecurityContext

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • Страница Функциональность

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

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

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

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

  • AIX: 5.3 или выше.

    FreeBSD: 7 или выше.

    Linux: LSB 3.1 (RHEL 4, SuSE 10) или выше.

    Solaris: 10 или выше.

    Windows 2000 или выше: Необходимо Windows 2000 SP4 или старше с Internet Explorer 6.0 или старше.

    Файл описания: Прототип описан в файле sspi.h для Windows и CSP_Sspi.h, CSP_SChannel.h, CpSSP.h для Unix.

    • КриптоПро SSPI Версия: 4.0