使用 WCF 进行相互 SSL 身份验证:握手阶段没有 CertificateRequest 和 CertificateVerify
Posted
技术标签:
【中文标题】使用 WCF 进行相互 SSL 身份验证:握手阶段没有 CertificateRequest 和 CertificateVerify【英文标题】:Mutual SSL authentication with WCF: no CertificateRequest and CertificateVerify in handshake phase 【发布时间】:2015-09-20 21:56:23 【问题描述】:我正在开发一个 WCF 服务,该服务将由不是我开发的客户端使用,而且它也不是 .NET(可能是 Java)。
在任何情况下,服务都应该支持相互 SSL 身份验证,其中服务和客户端都使用传输层的证书 X.509 证书进行身份验证。证书已在各方之间交换过。
我的问题是我似乎无法获得正确的 WCF 配置,以使客户端证书身份验证正常工作。我期望的是,作为 TLS 握手的一部分,服务器还包括一个Certificate Request
,如下所示:
在此之后,客户应回答“证书验证”等内容:
(最新的)服务配置就是这个。我正在使用自定义绑定,身份验证模式设置为MutualSslNegotiated
。
<bindings>
<customBinding>
<binding name="CarShareSecureHttpBindingCustom">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="MutualSslNegotiated"/>
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
...
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" httpHelpPageEnabled="false" />
<serviceCredentials>
<serviceCertificate findValue="..." storeLocation="LocalMachine" x509FindType="FindByIssuerName" storeName="My" />
<clientCertificate>
<certificate findValue="..." storeName="My" storeLocation="LocalMachine" x509FindType="FindByIssuerName"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
对于我尝试过的所有服务配置,握手的 Server Hello 部分看起来像这样,没有 CertificateRequest。
我应该提到的其他事情:
该服务是自托管的,并在非默认端口(不是 443)上侦听。服务器 SSL 证书已绑定到此端口。 我还尝试了basicHttpBinding
和wsHttpBidning
,安全模式设置为Transport
,客户端身份验证设置为Certificate
,但没有结果(实际上结果相同)。
任何想法将不胜感激。
【问题讨论】:
您是否尝试启用 WCF 跟踪并查看其中是否有任何详细错误? @UserControl:是的,跟踪已启用,但遗憾的是没有相关错误。 狂野 - 是否有可能在服务器端缺少来自客户端证书信任链的一些根证书?也就是说,你有客户端的证书,但是它链中的一些根证书丢失了......? @DavidW:谢谢大卫,但是由您描述的内容引起的错误会在 SSL 协商中稍后出现。无论如何,我今天已经解决了这个问题,但还没有时间发布答案。一会儿会做。一切都与 SSL 证书绑定到我使用的端口的方式有关。原来你必须手动启用客户端证书协商。显然 IIS 在禁用它的情况下创建了映射(甚至可能是端口 443 - 没有测试它),所以我不确定是什么。 啊,那好吧。很高兴你解决了! 【参考方案1】:好的,经过几次尝试后,我想通了。发布这个以防其他人遇到同样的问题。
我应该继续提到,这种行为确实需要在 MSDN 上的某个地方提及,该位置对于任何寻找 WCF 安全信息的人来说都是真正可见的,而不是深埋在某些工具的文档中。
我能够重现和修复此问题的平台:Windows 8.1 x64 和 Windows Server 2008 R2 Standard。
正如我所提到的,我的问题是我无法配置 WCF 安全性,以便该服务需要客户端证书。我在寻找解决方案时注意到的一个常见困惑是,许多人认为如果客户拥有证书,客户可以发送证书,而不会受到挑战。当然,情况并非如此 - 服务器需要首先请求它,此外,指定允许哪些 CA through a CertificateRequest
reply。
总结一下,我的情况是:
服务是自托管的。 服务在 HTTPS 上运行,在非标准端口(不是 443 而是 9000)上。这意味着我必须使用netsh.exe http add sslcert
为端口9000 创建一个SSL 证书绑定。好吧,绑定已经创建,但是有一个问题。我只是在运行netsh http show sslcert
后才发现问题,只是为了检查我的绑定:
IP:port : 0.0.0.0:9000
Certificate Hash : ...
Application ID : ...
Certificate Store Name : MY
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
-->Negotiate Client Certificate : Disabled
罪魁祸首是绑定的最后一个属性,“协商客户端证书”,记录在 here。显然,默认情况下,此属性是禁用的。您需要在创建绑定时显式启用它。
使用以下语句重新创建绑定解决了该问题:
netsh.exe http add sslcert ipport=0.0.0.0:9000 certhash=... appid=... certstorename=MY verifyclientcertrevocation=Enable VerifyRevocationWithCachedClientCertOnly=Disable UsageCheck=Enable clientcertnegotiation=Enable
在检查绑定之前,我尝试在 IIS 中托管一个简单的 WCF 服务并从那里启用客户端证书身份验证。很好奇的是,虽然 IIS 没有发出CertificateRequest
,但还是失败了403.7
。甚至 IIS 也没有使用适当的参数创建绑定。
无论如何,现在它可以工作了,这就是你可以修复它的方法。
不要忘记,服务配置也发生了变化(绑定安全),以允许证书协商:
<customBinding>
<binding name="CustomHttpBindingCustom" receiveTimeout="01:00:00">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="SecureConversation" requireSecurityContextCancellation="true">
<secureConversationBootstrap allowInsecureTransport="false" authenticationMode="MutualSslNegotiated" requireSecurityContextCancellation="true"></secureConversationBootstrap>
</security>
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
【讨论】:
【参考方案2】:当我的老板质疑为什么我们的 IIS 托管的 WCF 服务实现了“2 路 SSL”(mutual certificate authentication) 时,我遇到了同样的问题,而没有观察到在 TLS 握手中发送“证书请求”。经过一番排查,我们终于发现Negotiate Client Certificate的证书端口绑定配置被禁用了。
通过运行以下命令获取当前绑定信息。
netsh http show sslcert
从第一条记录(或相关 SSL 端口)获取证书哈希和应用程序 GUID,然后使用 IIS 服务器上的管理员控制台运行以下 netsh command。
netsh http add sslcert ipport=0.0.0.0:443 certhash=xxxx appid=xxxxx clientcertnegotiation=enable
注意如果IIS地址和端口已经存在绑定,会报如下错误。
SSL Certificate add failed, Error: 183 Cannot create a file when that file already exists.
在重新尝试重新添加之前,运行删除命令删除现有绑定。
netsh http delete sslcert ipport=0.0.0.0:443
重新配置后,观察到的 Wireshark TLS 握手变为expected。但是,在我看来,这个设置最终并不重要,因为无论是在初始握手期间还是之后在加密交换中,客户端证书都用于身份验证,并且实现了 2 路 SSL。
【讨论】:
以上是关于使用 WCF 进行相互 SSL 身份验证:握手阶段没有 CertificateRequest 和 CertificateVerify的主要内容,如果未能解决你的问题,请参考以下文章