使用 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 证书已绑定到此端口。 我还尝试了basicHttpBindingwsHttpBidning,安全模式设置为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的主要内容,如果未能解决你的问题,请参考以下文章

OpenSSL 的相互身份验证始终成功

SSL和TLS协议如何提供身份验证机密性和完整性

没有 SSL 的 WCF 身份验证

WCF与相互身份验证

IIS 托管具有 SSL 安全性的 WCF -“HTTP 请求被客户端身份验证方案‘匿名’禁止”错误

WCF 身份验证错误