通过 HTTPS 上的客户端证书对 WCF 请求进行身份验证

Posted

技术标签:

【中文标题】通过 HTTPS 上的客户端证书对 WCF 请求进行身份验证【英文标题】:Authentication a WCF Request via Client Certificate over HTTPS 【发布时间】:2011-05-20 01:24:41 【问题描述】:

在过去的一周里,我一直在为这个糟糕的 WCF 服务的配置而苦苦挣扎,我开始怀疑我正在尝试做的事情是不可能的,尽管有文档。

很简单,我希望 WCF 服务需要一个客户端证书(服务器将在其证书存储中拥有该证书),然后使用 System.ServiceModel.ServiceSecurityContext 访问该身份。此外,这需要使用传输安全性。

这是我的服务器配置:

<system.serviceModel>
    <services>
      <service behaviorConfiguration="requireCertificate" name="Server.CXPClient">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="wsHttpEndpoint" contract="PartnerComm.ContentXpert.Server.ICXPClient" />
        <endpoint address="mex" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="mexEndpoint" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost:8371/Design_Time_Addresses/Server/CXPClient/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="requireCertificate">
          <serviceMetadata httpsGetEnabled="true" />
          <serviceCredentials>
            <serviceCertificate findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName"/>
            <clientCertificate>
              <authentication certificateValidationMode="ChainTrust" trustedStoreLocation="LocalMachine" />
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpEndpointBinding" maxBufferPoolSize="5242880" maxReceivedMessageSize="5242880">
          <readerQuotas maxDepth="32" maxStringContentLength="5242880" maxArrayLength="1073741824" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
  </system.serviceModel>

这是我的客户端配置:

  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00"
          receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
          transactionFlow="false" hostNameComparisonMode="StrongWildcard"
          maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
          textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <reliableSession ordered="true" inactivityTimeout="00:10:00"
            enabled="false" />
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <client>
      <endpoint address="https://localhost:8371/Design_Time_Addresses/Server/CXPClient/"
        binding="wsHttpBinding" bindingConfiguration="wsHttpEndpoint" behaviorConfiguration="ClientCertificateBehavior"
        contract="ContentXPertServer.ICXPClient" name="wsHttpEndpoint" />
    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="ClientCertificateBehavior">
          <clientCredentials>
            <clientCertificate x509FindType="FindBySubjectName" findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" />
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>

当 security mode='None' over http 时,代码都可以完美运行,但当然,没有身份验证,System.ServiceModel.ServiceSecurityContext 中也没有任何内容。我已经对所有这些元素尝试了数十种变体,最终都不可避免地导致请求抛出异常“现有连接被远程主机强行关闭”。

我正在使用自签名证书“​​Cyber​​dyneIndustries”,我已将其 CA 证书添加到受信任的 CA 存储中。证书在我查看时签出。我经历了 http 命名空间管理的地狱,也解决了这些问题。看起来 WCF 并不真正支持这一点...请告诉我我错了。

TIA。

【问题讨论】:

【参考方案1】:

我知道这已经 3 岁了,但是对于那些可能仍然感兴趣的人......

我正在学习 WCF(除其他外的安全性),并且能够使用带有 clientCredentialType="Certificate" 的传输安全模式使 netTcpBinding 正常工作(大概这也适用于 WsHttpBindings) (以及 protectionLevel="EncryptAndSign",尽管这与问题无关)。

我也确实遇到了服务器端的强制连接关闭错误,但发现我缺少一项配置。现在一切正常。

这是我的服务器端配置:

<configuration>
  <system.serviceModel>
    <services>
      <service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
        <endpoint address="net.tcp://localhost:9002/MyServer" binding="netTcpBinding" bindingConfiguration="TcpCertSecurity" contract="MyNamespace.IMyService" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyServiceBehavior">
          <serviceCredentials>
            <serviceCertificate findValue="MyServiceCert" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
            <clientCertificate>
              <authentication certificateValidationMode="PeerTrust"/>
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="TcpCertSecurity">
          <security mode="Transport">
            <transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

还有我的客户端配置:

<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="net.tcp://localhost:9002/MyServer" binding="netTcpBinding"
          bindingConfiguration="TcpCertSecurity" contract="MyNamespace.IMyService"
          behaviorConfiguration="MyServiceBehavior">
        <identity>
          <dns value="MyServiceCert" />
        </identity>
      </endpoint>
    </client>
    <bindings>
      <netTcpBinding>
        <binding name="TcpCertSecurity">
          <security mode="Transport">
            <transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="MyServiceBehavior">
          <clientCredentials>
            <serviceCertificate>
              <authentication certificateValidationMode="PeerTrust" />
            </serviceCertificate>
            <clientCertificate findValue="MyServiceCert" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName" />
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

我使用here 描述的技术为服务器创建了一个证书链(自签名可信根证书 + 使用该根构建的证书),并将根证书和子证书存储在我的服务器主机的证书存储中机器。最后,我将该服务器证书 + 公钥导入客户端主机上的证书存储区(在 LocalMachine/TrustedPeople 中)。

【讨论】:

【参考方案2】:

最终,我决定尝试消息安全,看看这是否能对情况有所帮助 - 确实如此,我将减少损失并继续这样做。所以,这个问题没有明确的答案。

然而,实施消息安全确实暴露了一个大问题,这可能是传输安全问题的根源。有一段来自 MSDN 的毒药文档:

http://msdn.microsoft.com/en-us/library/ff650751.aspx

在这个页面上,创建自签名证书的命令如下:

makecert -sk MyKeyName -iv RootCaClientTest.pvk -n “CN=tempClientcert”-ic RootCaClientTest.cer -sr 当前用户 -ss 我的 -sky 签名 -pe

参数“签名”应改为“交换”。一旦我重新生成了所有证书,消息安全性就开始工作了。所有这一切的一大收获是,如果您想实现传输安全,请首先让消息安全工作,因为您从系统获得的错误消息更具描述性。

【讨论】:

【参考方案3】:

SSL 握手是否成功?启用 SChannel 日志记录以对 SSL 层进行故障排除。请参阅这篇旧知识库文章:How to enable Schannel event logging in IIS。虽然是 W2K 和 XP 的 KB,但启用 SChannel 日志记录的步骤是相同的​​,并且在较新的系统上仍然有效。启用日志记录后,您将能够确定 SSL 拒绝证书的原因。

【讨论】:

【参考方案4】:

WsHttpBinding 支持传输安全的证书身份验证。

可能有一些问题:

    您是否将两个证书添加到您的商店? Cyber​​dyneIndustries 以及您用来签署它的 CA? CA 应位于“受信任的根证书颁发机构”中

    另外,我已经完成了这个自托管,从未在 Visual Studio 开发服务器中。至少尝试在 IIS 中托管您的服务。我不确定 VS 开发服务器是否支持证书。

    尝试关闭服务验证。因此客户端不必对服务进行身份验证。我不知道您是否希望在您的应用中使用此功能,但仅用于测试,以便我们排除这种情况

    <behavior name="ClientCertificateBehavior">
    <clientCredentials>
        <clientCertificate x509FindType="FindBySubjectName" findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" />
        <serviceCertificate>
            <authentication certificateValidationMode="None"/>
        </serviceCertificate>
    </clientCredentials>
    

【讨论】:

以上是关于通过 HTTPS 上的客户端证书对 WCF 请求进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

在 Service Fabric 中通过 HTTPS 调用 WCF:请求被中止:无法创建 SSL/TLS 安全通道

WCF WS2007FederationHttpBinding 与 HTTPS

iOS https请求对自签名证书忽略

WCF 上的 CORS - 为啥在请求来自客户端机器时指定 Web 服务器?

使用ssl和客户端证书的Wcf:请求svc succes wcf调用返回403.16

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