如何强制WCF客户端发送客户端证书?

如何强制WCF客户端发送客户端证书?,wcf,https,certificate,ssl,Wcf,Https,Certificate,Ssl,我试图通过https访问一个公共托管的SOAP web服务(不是WCF),但我遇到了一个我以前从未见过的错误。首先,事实如下: 此服务需要客户端证书。我有一个证书,该证书由与服务器证书相同的CA签署 我知道URL是可用的,因为我可以在Internet Explorer中点击它。IE打开“选择证书”窗口,如果我选中它(忽略服务器主机名与证书不匹配的错误),它将继续并给我一个HTTP 500错误 如果我在Chrome中打开站点,在选择证书并忽略错误后,我会收到一条关于WSA Action=null

我试图通过https访问一个公共托管的SOAP web服务(不是WCF),但我遇到了一个我以前从未见过的错误。首先,事实如下:

  • 此服务需要客户端证书。我有一个证书,该证书由与服务器证书相同的CA签署
  • 我知道URL是可用的,因为我可以在Internet Explorer中点击它。IE打开“选择证书”窗口,如果我选中它(忽略服务器主机名与证书不匹配的错误),它将继续并给我一个HTTP 500错误
  • 如果我在Chrome中打开站点,在选择证书并忽略错误后,我会收到一条关于WSA Action=null的正常错误消息
  • 如果我在FireFox中打开站点,在忽略错误后,我会得到一个页面,说明服务器如何无法验证我的证书。它从来没有让我选一个,所以这很有道理
现在,例外情况是:

Error occurred while executing test 12302: System.ServiceModel.Security.SecurityNegotiationException: Could not establish secure channel for SSL/TLS with authority 'ihexds.nist.gov:9085'. ---> System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.
   at System.Net.HttpWebRequest.GetResponse()
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
我已经追踪了与WireShark的互动,但因为我不是TLS协议的专家,我可能会错过一些线索。然而,我看到的是:

  • C->S客户你好
    • 包含诸如随机数、日期/时间、支持的密码套件等内容
  • S->C服务器你好,证书,证书请求,服务器你好完成
    • 包含服务器的证书和对客户端证书的请求
  • C->S证书、客户端密钥交换、更改密码规范、加密握手消息
    • 这里是有趣的部分——这个数据包的第一部分是证书握手,我假设客户端证书是,但是没有证书(证书长度:0)
  • S->C警报(级别:致命,描述:坏证书)
    • 嗯,是的,没有发送证书
  • 我的绑定设置如下:

    <binding name="https_binding">
        <textMessageEncoding />
        <httpsTransport useDefaultWebProxy="false" />
    </binding>
    
    <behavior name="clientcred">
        <clientCredentials>
            <clientCertificate findValue="69b6fbbc615a20dc272a79caa201fe3f505664c3" storeLocation="CurrentUser" storeName="My" x509FindType="FindByThumbprint" />
            <serviceCertificate>
                <authentication certificateValidationMode="None" revocationMode="NoCheck" />
            </serviceCertificate>
        </clientCredentials>
        <messageInspector />
    </behavior>
    
    
    
    我的行为设置如下:

    <binding name="https_binding">
        <textMessageEncoding />
        <httpsTransport useDefaultWebProxy="false" />
    </binding>
    
    <behavior name="clientcred">
        <clientCredentials>
            <clientCertificate findValue="69b6fbbc615a20dc272a79caa201fe3f505664c3" storeLocation="CurrentUser" storeName="My" x509FindType="FindByThumbprint" />
            <serviceCertificate>
                <authentication certificateValidationMode="None" revocationMode="NoCheck" />
            </serviceCertificate>
        </clientCredentials>
        <messageInspector />
    </behavior>
    
    
    

    我的端点设置为同时使用绑定和行为。为什么WCF在创建https连接时拒绝发送证书?

    在协商使用哪个安全协议时可能会出现问题。具体来说,我认为服务器可能不喜欢WCF尝试使用TLS1.0

    要查看是否是这种情况,请在调用服务之前尝试添加以下内容

    System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3
    

    这可以添加到客户端代码中,也可以将其放置在IEndpointBehavior中。应该有来自服务器的CertificateRequest,命名可接受的证书类型和CA。如果您的证书与这些证书不匹配,它将不会被发送。

    我解决了这个问题,但我不明白为什么此配置更改会修复它。我改变了这一行:

    <httpsTransport useDefaultWebProxy="false" />
    
    
    
    为此:

    <httpsTransport useDefaultWebProxy="false" requireClientCertificate="true" />
    
    
    

    神奇的是,它开始工作了。我知道,
    requireClientCertificate
    “旋钮”是服务器端的,所以在争吵期间我没有试过。显然我错了。

    有一个证书请求(请参见#2),它确实包含CA和证书类型的列表。我确实有一个合适的证书,它是行为中指定的证书。如果所有这些都是真的,那么在服务器发出CertificateRequest之后,客户端就会发出一条证书消息。不可避免的结论是,其中一些是不真实的。您需要仔细检查所有内容:具体来说,证书(a)对应用程序可用,并且(b)符合CertificateRequest中表示的所有约束。这是可验证的事实。。。我在这里抓到了线鲨。虽然我最终解决了这个问题,但我真的不明白为什么我改变了它。我会在一个单独的答案中发布它。你确实收到了CertificateRequest消息,并且没有发送证书。这就是你上面的垃圾堆所显示的。如果有一个客户端证书消息,它就会出现在对请求的响应中。是的,我就是这么说的。整个问题归结为,“服务器请求了一个客户端证书,为什么WCF不发送一个?”我尝试了这个,结果没有改变。我知道服务器支持TLS 1.0(事实上,只有一个特定的密码,但该密码在握手时发送给服务器的列表中)。如果exchange收到ChangeCipherSpec消息,则不支持。这意味着协议的所有内容都已达成一致。我非常讨厌WCF。这个解决方案对我来说很有效,谢谢你——但它太复杂了。现在杀了我。