坏消息是,正如ILSpy所确定的那样,无法从ASP.NET内的任何地方到达System.Net.SslStream
实例.该类用于针对网络的直接编程,例如通过WCF框架.从ASP.NET(无论是使用System.Web还是在IIS或HttpListener之上使用OWIN),您能做的最好的事情就是获取一个服务器变量(see list of IIS server variables),以确定连接是否受到与客户端协商的任何安全传输的保护.
就在web请求期间从事件日志(log)中确定读取数据而言...这似乎很可怕.但如果你能让它工作,请分享代码.:)
或者,您可以try 实现自己的Owin主机(又名web服务器!)下面用SslStream
.也许吧P有关SslStream
编程的详细介绍,请参见this article.
但是,由于您已经能够关闭服务器上的某些协议(我假设是在this article中)...您可以在两个不同的子域上设置站点,例如www.example.com
和secure.example.com
,其中前者是一个普通的web服务器,后者配置为仅接受TLS 1.2连接.然后,您将编写一些 bootstrap 逻辑,从www.example.com
获得服务,并try 向secure.example.com/securityUpgradeCheck
发出AJAX请求(可能带有风格优美的微调器动画和"请稍候,try 保护此连接"文本,以打动您的用户:).如果该请求成功,用户可以重定向到secure.example.com
(可能是永久性的,因为该用户代理随后已知支持TLS 1.2,除非用户出于某种原因更改其浏览器设置).
为了增加影响,请为安全域订购EV SSL证书,这样您的用户就会注意到安全方面的升级.:)
在编写自定义(本地)ISAPI过滤(或扩展)以通过Schannel API获取此信息的理论基础上,我做了更深入的研究.起初我满怀希望,因为我发现了一个函数HSE_REQ_GET_SSPI_INFO
,它将返回SSPICtxtHandle
struct ,您可以通过EXTENSION_CONTROL_BLOCK
ServerSupportFunction
函数从自定义ISAPI扩展调用它.事实证明,CtxtHandle
struct 表示Schannel上下文,can为您提供对SECPKG_ATTR_CONNECTION_INFO
属性的引用,您可以使用该属性检索SSL连接级信息(据我所知,这与.NET的SslStream
类中出现的信息相同).然而,令人遗憾的是,Microsoft anticipated that possibility和this information is only available if you are using client certificates.决定,this information is only available if you are using client certificates.的行为是"有意为之".
我在长期搜索MSDN时发现了一个(本地)SSPI函数,即QueryContextAttributes (Schannel)
,其中may可以工作.我还没有try 过,它可能会因为与上面链接的ISAPI API限制相同的"设计"原因而失败.不过,这可能值得一试.如果你想探索这条路由,这里是an example of an ISAPI extension.实际上,使用这种方法,您可以使用较新的IIS 7.0+SDK编写IIS模块.
但是,假设你没有要求客户证书的奢侈条件,而且"远射"是行不通的,那绝对只剩下两种 Select .
- 使用不同的web服务器(Apache等),在相同的物理/虚拟机上运行,但在不同的端口上运行,等等(根据我们在 comments 中的讨论,因为您无法启动另一台机器).如果您只想给客户机一条信息性消息,那么这种方法加上AJAX请求可能就足够了.是的,一个不同的端口很可能会被某个地方的防火墙屏蔽,但嘿——无论如何,这只是一条可选的信息消息.
- 依赖于系统事件日志(log)的半脆性方法.Enable Schannel event logging,然后编写一些事件日志(log)查询代码,try 将请求与最后记录的Schannel事件相关联.请注意,您需要找到一种方法来可靠地将放入事件日志(log)中的任何内容与当前HTTP请求相关联,因此,您might还需要编写一个ISAPI过滤/扩展或IIS模块(在本例中为IIS模块)来查找SCANNEL上下文句柄(我假设相关性将基于此).
顺便问一下,您的负载平衡器是否配置为执行任何SSL拦截?因为这整件事无论如何都没有意义...只是考虑一下.
UPDATE:年来,Schannel logging取得了以下成就:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Schannel" Guid="{1F678132-5938-4686-9FDC-C8FF68F15C85}" />
<EventID>36880</EventID>
<Version>0</Version>
<Level>4</Level>
<Task>0</Task>
<Opcode>0</Opcode>
<Keywords>0x8000000000000000</Keywords>
<TimeCreated SystemTime="2014-08-13T02:59:35.431187600Z" />
<EventRecordID>25943</EventRecordID>
<Correlation />
<Execution ProcessID="928" ThreadID="12912" />
<Channel>System</Channel>
<Computer>**********</Computer>
<Security UserID="S-1-5-18" />
</System>
<UserData>
<EventXML xmlns:auto-ns3="http://schemas.microsoft.com/win/2004/08/events" xmlns="LSA_NS">
<Type>client</Type>
<Protocol>TLS 1.2</Protocol>
<CipherSuite>0x3c</CipherSuite>
<ExchangeStrength>2048</ExchangeStrength>
</EventXML>
</UserData>
</Event>
这可以直接从托管代码中读出.不幸的是,我认为userid只对应于IIS工作进程SID,但是假设您可以提出某种关联启发式方法,您可以设置一个后台线程来不断轮询事件日志(log),并为您提供最近建立的客户端握手列表(可能使用ConcurrentDictionary
).
那里就这样.我再也不好奇了.我做完了P