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

发布于 2024-10-06 03:29:22 字数 4332 浏览 1 评论 0原文

过去一周,我一直在努力配置这个该死的 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>

当 http 上的安全模式=“无”时,代码一切正常,但是当然,没有身份验证,System.ServiceModel.ServiceSecurityContext 中也没有任何内容。我已经尝试了所有这些元素的数十种变体,但最终都不可避免地导致请求抛出异常“现有连接被远程主机强制关闭”。

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

TIA。

I've been struggling with the configuration for this blasted WCF service for the past week, and I'm slowing beginning to suspect that what I'm trying to do is just not possible, despite the documentation.

Quite simply, I want to have a WCF service require a client certificate (which the server will have in its cert store), and then access that identity with System.ServiceModel.ServiceSecurityContext. Additionally, this needs to use transport security.

Here's my server config:

<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>

Here's my client config:

  <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>

The code all works perfectly when security mode='None' over http, but of course, there's no authentication, and nothing in System.ServiceModel.ServiceSecurityContext. I've tried dozens of variations on all of these elements, and it all ends up inevitably with the request throwing an exception "An existing connection was forcibly closed by the remote host".

I'm using a self-signed cert "CyberdyneIndustries", whose CA cert I've added to the trusted CA store. The cert checks out when I view it. I've gone through the hell of http namespace management, and solved those problems as well. It simply looks like WCF doesn't really support this...please tell me I'm wrong.

TIA.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

漫漫岁月 2024-10-13 03:29:22

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

然而,实现消息安全确实暴露了一个大问题,这可能是传输安全问题的根源。 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

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

Ultimately, I decided to try message security, to see if that would shed some light on the situation - it did, and I'm going to cut my losses and go with that. So, there's no definitive answer to this.

Implementing message security did, however, expose a BIG problem, and this may have been the root of the transport security problem. There is a piece of poison documentation from MSDN:

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

On this page, the command to create the self-signed cert is as follows:

makecert -sk MyKeyName -iv
RootCaClientTest.pvk -n
"CN=tempClientcert" -ic
RootCaClientTest.cer -sr currentuser
-ss my -sky signature -pe

The argument "signature" should instead be "exchange". Once I regenerated all my certs, message security started working. One big takeaway from all of this is that if you're wanting to implement transport security, get message security working first, because the error messages you get from the system are much more descriptive.

无尽的现实 2024-10-13 03:29:22

SSL握手是否成功?启用 SChannel 日志记录以排除 SSL 层故障。请参阅这篇旧知识库文章:如何在 IIS 中启用 Schannel 事件日志记录。尽管是适用​​于 W2K 和 XP 的 KB,但启用 SChannel 日志记录的步骤是相同的​​,并且在较新的系统上仍然有效。启用日志记录后,您将能够确定 SSL 拒绝证书的原因。

Does the SSL handshake succeed? Enable SChannel logging to troubleshoot the SSL layer. See this old KB article: How to enable Schannel event logging in IIS. Although is an KB for W2K and XP, the steps to enable SChannel logging are the same and still valid on newer systems. With the logging enabled you'll be able to determine why is SSL rejecting the certificate.

清醇 2024-10-13 03:29:22

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

我正在学习 WCF(安全性等)并且能够使用 netTcpBinding 使事情正常工作(大概,这会起作用)对于 WsHttpBindings 也是如此)使用带有 clientCredentialType="Certificate" 的传输安全模式(并且,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>

我使用描述的技术为服务器创建了一个证书链(自签名受信任的根证书+使用该根构建的证书) www.digitallycreated.net/Blog/38/using-makecert-to-create-certificates-for-development" rel="nofollow">此处 并将根证书和子证书存储在我的证书存储中服务器主机。最后,我将该服务器证书 + 公钥导入到客户端主机上的证书存储中(在 LocalMachine/TrustedPeople 中)。

I know this is 3 years old, but for those who might still be interested...

I'm in the process of learning WCF (security among other things) and was able to get things working properly with netTcpBinding (presumably, this will work for WsHttpBindings as well) using Transport security mode with a clientCredentialType="Certificate" (and, protectionLevel="EncryptAndSign", though that wasn't germane to the issue).

I did encounter the force connection close error from the server-side too, but discovered I was missing one piece of configuration. It's all working now.

Here's my server-side config:

<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>

And my client-side 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>

I created a certificate chain for the server (self-signed Trusted Root certificate + a certificate built using that root) using the technique described here and stored both the Root cert and child cert in the certificate store of my server host machine. And, finally, I imported that server certificate + public key into the cert store on my client host machine (in LocalMachine/TrustedPeople).

奢华的一滴泪 2024-10-13 03:29:22

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

可能存在一些问题:

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

  2. 此外,我已完成此自托管,从未在 Visual Studio Dev 服务器中进行。至少尝试在 IIS 中托管您的服务。我不确定 VS Dev 服务器是否支持证书。

  3. 尝试关闭服务身份验证。因此客户端不必验证服务。我不知道您是否希望在您的应用程序中使用此功能,但只是为了测试,以便我们排除这种情况

    <行为名称=“ClientCertificateBehavior”>
    <客户凭证>
        >
        <服务证书>
            <认证证书ValidationMode=“无”/>
        
    
    

WsHttpBinding DOES support certificate authentication for transport security.

There can be a few things wrong:

  1. Did you add both certificates to your store? CyberdyneIndustries as well a CA that you used to sign it? CA should be in "Trusted Root Certification Authorities"

  2. Also, i've done this self-hosted, never in Visual Studio Dev server. Try to host your service in IIS at least. I am not sure if VS Dev server supports certificates.

  3. Try to turn off service authentication. So the client doesn't have to authenticate the service. I don't know if you want this in your app or not but just for testing so we can rule that out

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文