Использование и устранение неполадок CryptAcquireContext()

В этой статье содержатся сведения об использовании определенных флагов при вызове CryptAcquireContext и причинах использования этих флагов.

Применяется к: Windows Server 2012 R2
Исходный номер базы знаний: 238187

Сводка

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

Дополнительная информация

Операции с закрытым ключом не выполняются

Если вы не используете сохраненный закрытый ключ, при вызове CryptAcquireContext можно использовать флаг CRYPT_VERIFYCONTEXT (0xF0000000). Это указывает CryptoAPI на создание контейнера ключей в памяти, который будет освобожден при вызове CryptReleaseContext. При использовании этого флага параметр pszContainer должен иметь значение NULL. Флаг CRYPT_VERIFYCONTEXT можно использовать в следующих сценариях:

  • Вы создаете хэш.

  • Вы создаете симметричный ключ для шифрования или расшифровки данных.

  • Вы наследуете симметричный ключ из хэша для шифрования или расшифровки данных.

  • Вы проверяете подпись. Можно импортировать открытый ключ из PUBLICKEYBLOB или из сертификата с помощью CryptImportKey или CryptImportPublicKeyInfo.

  • Вы планируете экспортировать симметричный ключ, но не импортировать его в течение времени существования контекста шифрования.

    Примечание.

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

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

Выполняются операции с закрытым ключом

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

Лучший способ получить контекст — попытаться открыть контейнер. Если эта попытка завершается с ошибкой "NTE_BAD_KEYSET", создайте контейнер с помощью флага CRYPT_NEWKEYSET.

Примечание.

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

// Acquire Context of container that is unique to each user.
if (!CryptAcquireContext(&hProv,  
 "Container",  
 NULL,  
 PROV_RSA_FULL,  
 0))
{
 if (GetLastError() == NTE_BAD_KEYSET)
 {
 if (!CryptAcquireContext(&hProv,  
 "Container",  
 NULL,  
 PROV_RSA_FULL,  
 CRYPT_NEWKEYSET))
 {
 // Error ...
 }
 }
}

// Or, acquire Context of container that is shared across the machine.
if (!CryptAcquireContext(&hProv,  
 "Container",  
 NULL,  
 PROV_RSA_FULL,  
 CRYPT_MACHINE_KEYSET))
{
 if (GetLastError() == NTE_BAD_KEYSET)
 {
 if (!CryptAcquireContext(&hProv,  
 "Container",  
 NULL,  
 PROV_RSA_FULL,  
 CRYPT_NEWKEYSET|CRYPT_MACHINE_KEYSET)
 {
 // Error ...
 }
 }
}

Использование флага CRYPT_MACHINE_KEYSET

Если вы не выполняете операции с закрытым ключом для каждого пользователя и вам нужны глобальные операции с закрытым ключом, следует использовать CRYPT_MACHINE_KEYSET. Этот метод создает пару закрытых и открытых ключей для каждого компьютера. Ниже приведены некоторые сценарии, в которых следует использовать CRYPT_MACHINE_KEYSET:

  • Вы пишете службу.
  • Компонент выполняется на странице Страницы активного сервера (ASP).
  • Ваш компонент является компонентом Microsoft Transaction Server (MTS). В этих примерах используется CRYPT_MACHINE_KEYSET, так как контекст безопасности, в котором выполняется приложение, не имеет доступа к профилю пользователя. Например, клиент MTS может олицетворить пользователя, но профиль пользователя недоступен, так как пользователь не вошел в систему. То же самое относится и к компоненту, который выполняется на странице ASP.

Предоставление доступа к контейнеру

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

Если код будет выполняться в нескольких контекстах безопасности, необходимо предоставить соответствующим пользователям доступ к контейнеру.

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

В следующем коде показано, как вызвать CryptSetProvParam. Это делается сразу после создания контейнера ключей.

// Acquire Context  
if (!CryptAcquireContext(&hProv,  
 "Container",  
 NULL,  
 PROV_RSA_FULL,  
 0))
{
 if (GetLastError() == NTE_BAD_KEYSET)
 {
 if (!CryptAcquireContext(&hProv,  
 "Container",  
 NULL,  
 PROV_RSA_FULL,  
 CRYPT_NEWKEYSET))
 {
 // Error ...
 }

// Create Security Descriptor (pSD)...

// Set the Security Descriptor on the container
 if (!CryptSetProvParam(hProv,
 PP_KEYSET_SEC_DESCR,
 pSD,
 DACL_SECURITY_INFORMATION))
 {
 // Error ...
 }
 }
}

Ошибки CryptAcquireContext

Ниже приведены наиболее распространенные коды ошибок и возможные причины ошибки.

  • NTE_BAD_KEYSET (0x80090016)
    • Контейнер ключей не существует.
    • У вас нет доступа к контейнеру ключей.
    • Служба защищенного хранилища не запущена.
  • NTE_EXISTS (0x8009000F)
    • Контейнер ключей уже существует, но вы пытаетесь создать его. Если предыдущая попытка открыть ключ завершилась сбоем с NTE_BAD_KEYSET, это означает, что доступ к контейнеру ключей запрещен.
  • NTE_KEYSET_NOT_DEF (0x80090019)
    • Поставщик служб шифрования (CSP) может быть настроен неправильно. Использование Regsvr32.exe в библиотеках DLL CSP (Rsabase.dll или Rsaenh.dll) может устранить проблему в зависимости от используемого поставщика.