Некоторые фоне первого
После вызова SslStream.AuthenticateAsClient() инициировать TLS/SSL рукопожатия, пользователь может быть представлен с этим следующим «безопасности Windows» диалог:Установка владельца диалогового «Безопасность Windows» в течение сертификата клиента с участием SslStream.AuthenticateAsClient()
безопасности для Windows: Это приложение должно использовать криптографический ключ
Это происходит, когда оба эти следующие причины встречаются:
- Сервер SSL, к которому клиент пытается подключиться к запросила клиента сертификат как часть/SSL рукопожатия TLS.
- The X509Certificate прошел через второй аргумент SslStream.AuthenticateAsClient() или через LocalCertificateSelectionCallback обратного вызова (при заданном constructing SslStream) был strongly protected. (Сам диалог будет отличаться в зависимости от так называют Уровень безопасности защиты.)
На более поздних версиях Windows (Win8 и Win10), этот диалог представлен внешней программой под названием "CredentialUIBroker.exe ", который вызывается svchost.exe. В более ранних версиях оно было представлено DLLs загружается в самой запущенной программы: comctl32.dll в Win7 и WinXP
Проблема
Хотя это диалоговое окно Безопасность Windows, как представляется cryptui.dll модальный диалог, он ведет себя скорее как модальный диалог без набора параметров владельца.
Это вызывает следующие проблемы:
- диалог может (и часто делает) открытой за окнами запущенной программы, тем самым делая самому трудно определить для пользователя.
- Диалог может быть скрыт в фоновом режиме, щелкнув на других окнах запущенной программы, что приведет к путанице.
- Элементы пользовательского интерфейса в других окнах запущенной программы не замерзают, когда диалог открыт, и пользователь может выполнять другие действия.
Вопрос в следующем: Как настроить все, чтобы диалоговое окно Windows Security представлялось в виде модального диалога?
Проблема, как показано в других программах
Chrome страдает от этой проблемы и не фиксируется на сегодняшний день (Chrome 51) (Bug трек: https://bugs.chromium.org/p/chromium/issues/detail?id=304152)
Internet Explorer не страдает от этой проблемы. Он представляет диалог Windows Security как модальный диалог.
Firefox неприменим, поскольку он никогда не использовал хранилища сертификатов Windows, полагаясь вместо этого на собственное хранилище.
Код для воспроизведения
Получение, что Windows Security UI, чтобы показать немного вовлечен.
Во-первых, для этого требуется сертификат, который был импортирован с помощью надежной защиты , указанной во время импорта интерфейса. (На стороне записки: Любой используемый сертификат должен быть неэкспортируемым, так как решение, которое работает только для экспортируемых сертификатов не подходит для производства.)
Этого код ниже, также необходим сертификат сервера (любой сертификат без сильных защита будет), потому что мы используем SslStream.AuthenticateAsClientAsync()
в поддельном TLS/SSL-соединении.
Кроме того, FullDuplexPipeStream
, используемый ниже, является основанием Stream, которое не включено здесь, потому что это МНОГО кодировки.
X509Certificate2 ServerCertificate = ...;
async Task Test(X509Certificate2 clientCertificate)
{
using (var serverStream = new FullDuplexPipeStream())
using (var clientStream = new FullDuplexPipeStream(serverStream))
using (var sslClientStream = new SslStream(clientStream, false,
(o, x509Certificate, chain, errors) => true,
(o, host, certificates, certificate, issuers) => clientCertificate))
using (var sslServerStream = new SslStream(serverStream, false,
(o, certificate, chain, errors) => true))
{
((Func<Task>)(async() =>
{
try
{
await sslServerStream.AuthenticateAsServerAsync(ServerCertificate,
true, SslProtocols.Tls, false);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}))();
await sslClientStream.AuthenticateAsClientAsync("foobar");
}
}
Полученный код работает в .Net 4.5+, чтобы воспроизвести диалог «Безопасность Windows». Он не будет работать в .Net 4.0 из-за использования async/wait. (Но это может быть сделано, чтобы работать с небольшим изменением, что заминки AuthenticateAsServer()
на другой поток.)
Кода для воспроизведения намного проще в .NET 4.6 в связи с добавлением RSACertificateExtensions.GetRSAPrivateKey() и RSACng.SignHash():
clientCertificate.GetRSAPrivateKey()
.SignHash(new byte[20], HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)
Хотя этот код больше не упоминает SSL, я уверен, что это то же самое, что и SslStream (или Secure Channel) делает за кулисами.
Дальнейшие исследования
Я уже упоминал RSACng.SignHash()
** производит очень похожий диалог смотрит. Кажется, он вызывает функцию Win32 NCryptSignHash().
(** RSACng
доступен только в .Net 4.6. Основанный на CAPI (CryptoAPI) (?) RSA.SignHash()
, который был доступен до .Net 4.6, показывает более унаследованный диалог.)
Взгляд на документации для NCryptSignHash()
имеет интересные пикантный о NCRYPT_SILENT_FLAG
флаге:
запросы, что ключ поставщик услуг (KSP) не отображает пользовательский интерфейс. Если провайдер должен отображать интерфейс для работы, вызов завершается с ошибкой, и KSP должен установить код ошибки
NTE_SILENT_CONTEXT
в качестве последней ошибки.
Кроме того, документация к CRYPT_ACQUIRE_ WINDOWS_HANDLE_FLAG
флаг для CryptAcquireCertificatePrivateKey выглядит многообещающим:
Любой пользовательский интерфейс, который необходим ПСУ или KSP будет потомком
HWND
, который подается в параметре pvParameters. Для ключа CSP использование этого флага вызовет функцию CryptSetProvParam с флагомPP_CLIENT_HWND
, используя этотHWND
для вызова сNULL
для HCRYPTPROV. Для ключа KSP использование этого флага приведет к вызову функции NCryptSetProperty с флагомNCRYPT_WINDOW_HANDLE_PROPERTY
с использованиемHWND
. Не используйте этот флаг сCRYPT_ACQUIRE_SILENT_FLAG
.
И вот-и-behond, я думаю, что это NCRYPT_WINDOW_HANDLE_PROPERTY свойства, установить через NCryptSetProperty(), что мне нужно, чтобы решить эту проблему.
Таким образом, для возможного решения: Любой код, который начинается с HWND или WPF Window и, возможно, с участием P/Invoke, устанавливает NCRYPT_WINDOW_HANDLE_PROPERTY
является жизнеспособным решением.
В идеале (для меня как минимум) код должен также работать в .Net 4.5.
Однако я не мог найти упоминания о CNG в .Net 4.5, поэтому я думаю, что это, скорее всего, связано с P/Invoking. Возможно, в .Net 4.6 есть управляемое решение.