设置“ Windows安全性”。涉及SslStream.AuthenticateAsClient()的客户端证书期间的对话框所有者 [英] Setting the "Windows Security" dialog owner during a Client Certificate involving SslStream.AuthenticateAsClient()

查看:189
本文介绍了设置“ Windows安全性”。涉及SslStream.AuthenticateAsClient()的客户端证书期间的对话框所有者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

某些背景优先



在调用



Windows安全:此应用程序需要使用加密密钥



满足以下两个原因就会发生这种情况:





在Windows的更高版本(Win8和Win10)上,此对话框由名为 CredentialUIBroker.exe 的外部程序显示,该程序由 svchost.exe 调用。在早期版本中,它是由加载到正在运行的程序本身的dll呈现的:Win7中的 comctl32.dll 和WinXP中的 cryptui.dll



问题



尽管此Windows安全对话框似乎是模式对话框,但其行为更类似于模式对话框,而没有所有者参数集



这会导致以下问题:




  • 对话框可以(并且经常确实)在正在运行的程序的窗口后打开,从而使用户难以发现自己。

  • 该对话框可以通过用户单击其他对话框隐藏在后台。

  • 打开对话框时,不会冻结正在运行程序其他窗口上的UI元素,用户可以自由执行其他操作。



所以问题是:如何进行设置,以便Windows安全对话框显示为模式对话框?



在其他软件中看到的问题




  • Chrome遭受此问题困扰,但尚未解决(Chrome 51)(错误轨道: https://bugs.chromium.org/p/chromium/issues/detail?id=304152


  • Internet Explorer可以不遭受这个问题。它将Windows安全性对话框显示为模式对话框。


  • Firefox不适用,因为它从未使用过Windows的证书存储,而是依赖于其自己的存储。




要复制的代码



获得该Windows安全性要显示的用户界面有点涉及。



首先,它要求使用导入UI时选中了强保护选项导入的证书。 (附带说明:使用的任何证书也应该是不可导出的,因为仅适用于可导出证书的解决方案不适用于生产。)



下面的此代码也需要服务器证书(没有强保护的任何证书都可以),因为我们在伪造的TLS / SSL中使用 SslStream.AuthenticateAsClientAsync()



此外,下面使用的 FullDuplexPipeStream FIFO队列基于 Stream 不在此处,因为它是很多样板代码。

  X509Certificate2 ServerCertificate = ...; 

异步任务测试(X509Certificate2 clientCertificate)
{
using(var serverStream = new FullDuplexPipeStream())
using(var clientStream = new FullDuplexPipeStream(serverStream))
使用(var sslClientStream = new SslStream(clientStream,false,
(o,x509Certificate,chain,errors)=> gt; true,
(o,主机,证书,证书,颁发者)=> ; clientCertificate))
using(var sslServerStream = new SslStream(serverStream,false,
(o,certificate,chain,errors)=> true))
{
(( Func< Task>)(async()=>
{
try
{
等待sslServerStream.AuthenticateAsServerAsync(ServerCertificate,
true,SslProtocols.Tls,false) ;
}
catch(例外)
{
Console.WriteLine(ex);
}
}))();

等待sslClientStream.AuthenticateAsClientAsync( foobar);
}
}

生成的代码在.Net 4.5+中可以重现 Windows安全性对话框。由于使用async / await,它在.Net 4.0中将无法工作。 (但是可以进行一些细微改动,将 AuthenticateAsServer()挂接到其他线程上。)



由于添加了 RSACertificateExtensions.GetRSAPrivateKey() RSACng.SignHash()

  clientCertificate .GetRSAPrivateKey()
.SignHash(new byte [20],HashAlgorithmName.SHA1,RSASignaturePadding.Pkcs1)

尽管此代码不再提及SSL,但我相当确定它与SslStream(或安全频道

进一步的研究



我已经提到了 RSACng.SignHash() **会产生一个非常相似的对话框。它似乎正在调用Win32函数 NCryptSignHash()



(** RSACng 仅在.Net 4.6及更高版本中可用。在.Net 4.6之前可用的基于CAPI(CryptoAPI)的(?) RSA.SignHash()显示了外观更传统的对话框。)



看看 NCryptSignHash()的文档,有关于 NCRYPT_SILENT_FLAG 的有趣提示。标志:


请求密钥服务提供者(KSP)不显示任何用户界面。如果提供程序必须显示UI才能操作,则调用将失败,KSP应将 NTE_SILENT_CONTEXT 错误代码设置为最后一个错误。


此外,CRYPT_ACQUIRE_ WINDOWS_HANDLE_FLAG 标志的文档zh-cn / library / windows / desktop / aa379885(v = vs.85).aspx rel = nofollow noreferrer> CryptAcquireCertificatePrivateKey 看起来很有希望:


CSP或KSP所需的任何UI都是pvParameters参数中提供的 HWND 的子级。对于CSP密钥,使用此标志将导致使用该 HWND 调用带有标志 PP_CLIENT_HWND 的CryptSetProvParam函数。 HCRYPTPROV的 NULL 。对于KSP密钥,使用此标志将导致使用 HWND 调用带有 NCRYPT_WINDOW_HANDLE_PROPERTY 标志的NCryptSetProperty函数。
不要将此标志与 CRYPT_ACQUIRE_SILENT_FLAG 一起使用。


-behond,我认为这 NCRYPT_WINDOW_HANDLE_PROPERTY 属性,可通过 NCryptSetProperty()是我需要解决的问题。



因此,可能的解决方案:任何以HWND或 WPF窗口,并且可能涉及P / Invoke,设置 NCRYPT_WINDOW_HANDLE_PROPERTY 是可行的解决方案。



理想情况下(至少对我而言),该代码也应在.Net 4.5中运行。



但是,我在中找不到任何提及CNG的信息。 Net 4.5,因此我认为它很可能涉及P / Invoking。也许在.Net 4.6中有一个托管解决方案。

解决方案

.Net中的修复程序(有点) 4.6实际上很简单:

  var rsa =(RSACng)clientCertificate.GetRSAPrivateKey(); 
rsa.Key.ParentWindowHandle = MyForm.Handle;

rsa.SignHash(新字节[20],HashAlgorithmName.SHA1,RSASignaturePadding.Pkcs1);

(我从.Net 4.0开始就一直在研究这个问题,谁知道.Net 4.6会



注意,这只是修复的有点,因为它不能直接解决如图所示的UI问题 SslStream.AuthenticateAsClient()。但是,通过执行上述 before SslStream.AuthenticateAsClient(),CNG会缓存用户的授权,并且在TLS / SSL握手过程中不会显示该对话框。



(这就是为什么解决方案需要基于CNG而不是较旧的CAPI的原因。)





不幸的是,我仍然需要支持.Net 4.5,因此在那里的解决方案将非常感谢


Some background first

Upon calling SslStream.AuthenticateAsClient() to initiate TLS/SSL handshake, the user can be presented with this following "Windows Security" dialog:

Windows security: This application needs to use a cryptographic key

This happens when both these following reasons are met:

On later versions of Windows (Win8 and Win10), this dialog is presented by an external program called "CredentialUIBroker.exe" that's invoked by svchost.exe. On earlier versions, it was presented by dlls loaded into the running program itself: comctl32.dll in Win7 and cryptui.dll in WinXP

The problem

While this Windows Security dialog appears to be a modal dialog, it behaves more like a modal dialog without the owner parameter set.

This causes the following problems:

  • The dialog can (and often does) open behind windows of the running program, thereby making itself hard to spot for the user.
  • The dialog can be hidden in the background, by the user clicking on other windows of the running program, leading to confusion.
  • UI elements on other windows of the running program aren't frozen while the dialog is open, and the user is free to perform other actions.

So the question is: How does one set things up so that the Windows Security dialog is presented as a modal dialog?

The problem as seen in other software

  • Chrome suffers from this problem and is not fixed to date (Chrome 51) (Bug track: https://bugs.chromium.org/p/chromium/issues/detail?id=304152)

  • Internet Explorer does not suffer from this problem. It presents the Windows Security dialog as a modal dialog.

  • Firefox is non-applicable as it has never used Windows' Certificate stores, relying instead on its own storage.

Code to reproduce

Getting that Windows Security UI to show is a bit involved.

First, it requires a certificate that was imported with the strong protection option checked during the import UI. (On a side note: Any certificate used should also be non-exportable, because a solution that only works for exportable certificates is unsuitable for production.)

This code below also requires a server certificate (any certificate without strong protection will do), because we're using SslStream.AuthenticateAsClientAsync() in a fake TLS/SSL connection.

Moreover, FullDuplexPipeStream used below is a FIFO queue based implementation of Stream that isn't included here because it's A LOT of boilerplate coding.

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

The resulting code works in .Net 4.5+ to reproduce the "Windows Security" dialog. It will not work in .Net 4.0 due to the use of async/await. (But it can be made to work with a slight alteration that hitches AuthenticateAsServer() onto a different thread.)

The code to reproduce is much easier in .Net 4.6 due to the addition of RSACertificateExtensions.GetRSAPrivateKey() and RSACng.SignHash():

clientCertificate.GetRSAPrivateKey()
    .SignHash(new byte[20], HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1)

Although this code no longer has any mention of SSL, I'm fairly sure it's the same thing as what SslStream (or Secure Channel) is doing behind the scenes.

Further research

I've already mentioned RSACng.SignHash() ** produces a very similar looking dialog. It seems to be calling the Win32 function NCryptSignHash().

(** RSACng is only available in .Net 4.6 onwards. CAPI (CryptoAPI) based (?) RSA.SignHash() that was available prior to .Net 4.6 shows a more legacy-looking dialog.)

A look at the documentation for NCryptSignHash() has this interesting tidbit about the NCRYPT_SILENT_FLAG flag:

Requests that the key service provider (KSP) not display any user interface. If the provider must display the UI to operate, the call fails and the KSP should set the NTE_SILENT_CONTEXT error code as the last error.

Furthermore, documentation to the CRYPT_ACQUIRE_ WINDOWS_HANDLE_FLAG flag for CryptAcquireCertificatePrivateKey looks promising:

Any UI that is needed by the CSP or KSP will be a child of the HWND that is supplied in the pvParameters parameter. For a CSP key, using this flag will cause the CryptSetProvParam function with the flag PP_CLIENT_HWND using this HWND to be called with NULL for HCRYPTPROV. For a KSP key, using this flag will cause the NCryptSetProperty function with the NCRYPT_WINDOW_HANDLE_PROPERTY flag to be called using the HWND. Do not use this flag with CRYPT_ACQUIRE_SILENT_FLAG.

And lo-and-behond, I think this NCRYPT_WINDOW_HANDLE_PROPERTY property, set via NCryptSetProperty(), is what I need to fix this problem.

So for possible solution: Any code that starts with an HWND or a WPF Window and, possibly involving P/Invoke, sets NCRYPT_WINDOW_HANDLE_PROPERTY is a viable solution.

Ideally (for me at least), the code should also work in .Net 4.5.

However, I couldn't find any mention of CNG in .Net 4.5 so I think it will most likely involve P/Invoking. Maybe there's a managed solution in .Net 4.6.

解决方案

A fix (somewhat) in .Net 4.6 is actually quite simple:

var rsa = (RSACng)clientCertificate.GetRSAPrivateKey();
rsa.Key.ParentWindowHandle = MyForm.Handle;

rsa.SignHash(new byte[20], HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);

(I've actually been looking at this problem since .Net 4.0 and who knew .Net 4.6 would make it so easy!)

Note, this is only somewhat of a fix because it doesn't directly solve the problem for UIs shown by SslStream.AuthenticateAsClient(). However, by performing the above before SslStream.AuthenticateAsClient(), CNG caches the user's grant and the dialog is not shown during the TLS/SSL handshake.

(This is why the solution needs to be CNG based instead of the older CAPI.)

The retention of this cache is configured via group policy, so this may not work in every environment.

Unfortunately, I still need to support .Net 4.5, so a solution there would be greatly appreciated

这篇关于设置“ Windows安全性”。涉及SslStream.AuthenticateAsClient()的客户端证书期间的对话框所有者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆