Ответ 1
Проблема, которую вы описываете, мне кажется очень интересной, но без дополнительной информации и некоторых экспериментов трудно окончательно сказать, в чем причина. Поэтому я пытаюсь описать, как я понимаю проблему.
Прежде всего, классы криптографии .NET используют внутренне неуправляемый CryptoAPI. Таким образом, метод _OpenCSP
вызывает внутренне функцию CryptAcquireContext. В своей документации мы можем прочитать следующую информацию об ошибке NTE_BAD_KEY_STATE
(0x8009000BL):
Пользовательский пароль изменился с тех пор секретные ключи были зашифрованы.
Пользователи частные ключи, используемые поставщиком DSA, сохраняются как файлы в каталоге %APPDATA%\Microsoft\Crypto\DSS\[SID]
и будут зашифрованы относительно сложным алгоритмом, о котором вы можете прочитать здесь. Важно понимать, что файлы из каталога соответствуют контейнерам ключей пользовательских ключей. Обычно пользователь имеет полный доступ к файлам в файловой системе. Файлы будут зашифрованы ключом, зависящим от пароля пользователя. Во многих стандартных случаях файлы будут заново зашифрованы после смены пароля, но алгоритм восстановления, который зависит от многих факторов. Если пароль был reset вместо изменения самим пользователем (от администратора домена/учетной записи домена и т.д.), Старый список из каталога %APPDATA%\Microsoft\Crypto\DSS\[SID]
может быть не более полезным. Например, если пользователь не является пользователем Active Directory (локальным пользователем) и локальным администратором reset его пароль, тогда проблема с криптоконтейнерами будет иметь место.
Итак, первое предложение - спросить у пользователя, был ли его пароль Active Directory reset. Затем вы должны убедиться, что каталог %APPDATA%\Microsoft\Crypto\DSS\[SID]
существует в профиле пользователя, и пользователь имеет полный доступ к каталогу в файловой системе. Вы должны удалить все файлы из каталога (создавая ранее резервную копию файлов). Кстати, интересно узнать, имеет ли пользователь центральный сохраненный профиль (сохраненный на сервере). Если у него есть центральный профиль, можно проверить, что одна и та же проблема, которую вы описываете, существует на другом компьютере для пользователя и другого пользователя, не будет иметь проблем на его исходном компьютере.
Еще один вопрос, который для меня не совсем понятен, - это почему контейнер ключей из каталога %APPDATA%\Microsoft\Crypto\DSS\[SID]
используется вообще, потому что вы используете только общедоступные ключи. В CryptoAPI следует использовать CryptAcquireContext
с NULL
как параметр pszContainer
и CRYPT_VERIFYCONTEXT
в dwFlags
. Я не уверен, что .NET использует флаг CRYPT_VERIFYCONTEXT
, и это может быть косвенным для вашей проблемы.
Вы можете создать DSACryptoServiceProvider
с конструктором с параметром CspParameters. CspParameters с другой стороны имеет свойство Flags, которое расширено в .NET 4.0 со значением CreateEphemeralKey. Описание CspProviderFlags.CreateEphemeralKey
очень близко к описанию флага CRYPT_VERIFYCONTEXT
функции CryptAcquireContext
. Таким образом, использование может использовать CspProviderFlags.CreateEphemeralKey или CspProviderFlags.CreateEphemeralKey
вместе с CspProviderFlags.UseDefaultKeyContainer
(NULL
как pszContainer
параметр CryptAcquireContext
означает также контейнер ключей по умолчанию).
Кроме того, если это возможно, вы можете попробовать отладить проблему на компьютере, где проблема может быть воспроизведена. Для отладки вы можете использовать источники .NET, которые можно включить (см. здесь и здесь) или скачан из здесь. Затем вы можете ответить на некоторые вопросы о значениях CspParameters
, которые в настоящее время используются в вашей программе, и сравнить значения для .NET 3.5 и .NET 4.0.
Если то, что я написал, не поможет решить проблему, пожалуйста, вы можете добавить свой вопрос с дополнительной информацией:
- В какой операционной системе и в каком пакете обновления есть компьютер, на котором проблема может быть воспроизведена?
- Является ли проблема зависимой от пользователя? Я имею в виду: есть ли другие пользователи на одном компьютере с той же проблемой?
- Проблема связана с пользователем домена (активным каталогом) или с локальной учетной записью пользователя? У пользователя, у которого есть проблема с центральным профилем пользователя, который сохраняется на сервере? Если он имеет, то может ли проблема может быть воспроизведена и на других компьютерах пользователем?
- Не могли бы вы описать немного больше среды, в которой вы используете проверку открытого ключа? Особенно, что программа работает в контексте безопасности пользователя или вы делаете какое-то олицетворение?
ОБНОВЛЕНО: После прочтения форума, на котором проблема была первоначально опубликована, я пессимистично отношусь к решению проблемы. Если у вас нет прямого контакта с компьютером, где проблема может быть воспроизведена, и связь с единственным пользователем, у которого есть проблема, выполняется только при размещении на форуме... Тем не менее, я думал о проблеме, и поэтому решил попытайтесь воспроизвести проблему самостоятельно. И у меня был успех в этом. Поэтому я подробно опишу здесь свои результаты. Я опишу, как может воспроизвести проблему, чтобы она выглядела точно так, как описано в в теме форума.
Вы должны сделать следующие шаги:
1) Вы создаете локальную тестовую учетную запись на своем компьютере. 2) Вход в систему с тестовой учетной записью и создание контейнера ключей по умолчанию для поставщика DSA. Вы можете сделать это, например, в отношении небольшой .NET-программы, вызывающей следующую простую функцию:
static string GenerateDsaKeyInDefaultContainer()
{
const int PROV_DSS_DH = 13;
CspParameters cspParam = new CspParameters(PROV_DSS_DH);
cspParam.KeyContainerName = null;
cspParam.KeyNumber = (int)KeyNumber.Signature;
cspParam.Flags = CspProviderFlags.UseDefaultKeyContainer;
DSACryptoServiceProvider csp = new DSACryptoServiceProvider(cspParam);
return csp.CspKeyContainerInfo.UniqueKeyContainerName;
}
функция возвращает имя файла, который будет создан в каталоге %APPDATA%\Microsoft\Crypto\DSS\[SID]
и который будет содержать сгенерированную пару ключей.
3) Вы выходите из тестовой учетной записи и авторизуетесь с другой учетной записью с правами локального администратора. Вы reset пароль тестовой учетной записи.
4) Вы снова входите в тестовую учетную запись и проверяете, что тестовая программа, которую вы опубликовали, скомпилированная в Visual Studio 2010 для .NET 4.0, выдает ошибку NTE_BAD_KEY_STATE
(0x8009000b), и соответствующее исключение будет выбрано.
5) Если вы перекомпилируете программу для .NET 3.5 вместо .NET 4.0 (вы также можете использовать Visual Studio 2010), тестовая программа будет запущена без каких-либо ошибок.
Таким образом, все результаты будут точно такими, какие описаны в теме форума.
Если вы удалите или переименуете файл с контейнером ключей по умолчанию для поставщика DSA, проблема будет решена. Как я описал ранее после сброса пароля пользователя, содержимое контейнера ключей по умолчанию не может быть расшифровано. Поэтому, если вы знаете имя контейнера по умолчанию, уникального для пользователя (вы можете увидеть это имя, например, из следов Process Monitor), вы можете просто скопировать любой файл контейнера ключей от любого другого пользователя и любого другого компьютера в каталог %APPDATA%\Microsoft\Crypto\DSS\[SID]
, переименуйте файл, чтобы имя было именем контейнера по умолчанию и... у вас будут абсолютно такие же результаты, как при сбросе пользователей пароль.
Я провел несколько экспериментов с различными настройками CspParameters
в качестве параметра DSACryptoServiceProvider
(см. мои первые предложения об использовании CspProviderFlags.CreateEphemeralKey), но без каких-либо успехов. После этого я отлаживал исходный код .NET 4.0 и могу окончательно сказать, что вызов функции _OpenCSP
, который не открывается, будет вызываться с параметрами independent из параметров конструктор DSACryptoServiceProvider
. Таким образом, не удается найти обходной путь проблемы для .NET 4.0 с различными настройками CspParameters
в качестве параметра DSACryptoServiceProvider
.
Итак, если вы хотите изменить свой код, чтобы он также работал в ситуации с поврежденным поставщиком ключей по умолчанию, я вижу в настоящее время только 2 способа решения проблемы:
- Внедрить целое или часть кода с помощью неуправляемой функции
CryptAcquireContext
с флагомCRYPT_VERIFYCONTEXT
. - Определите проблему с ошибкой
NTE_BAD_KEY_STATE
(0x8009000b) и включите часть кода, которая удаляет или временно переименовывает файл из%APPDATA%\Microsoft\Crypto\DSS\[SID]
, который содержит поврежденный контейнер ключей по умолчанию. Чтобы определить имя файла, я думаю, вы можете попробовать использовать функциюCryptGetProvParam
с параметрамиPP_UNIQUE_CONTAINER
или/иPP_ENUMCONTAINERS
.
Прошу прощения за длинный текст моего ответа и спасибо всем, кто умеет читать его до этого места.: -)
Я надеюсь, что мой ответ поможет вам KristoferA решить проблему и улучшить программное обеспечение, которое вы разрабатываете.