通过客户端证书在HTTPS上进行WCF请求的认证

9

我最近一周一直在苦恼这个可恶的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>

当安全模式为'None'并使用http时,代码完美运行,但是当然没有身份验证,也没有System.ServiceModel.ServiceSecurityContext中的任何内容。我已经尝试了这些元素的数十个变化,最终都会出现请求抛出异常“远程主机强制关闭了一个现有的连接”。我正在使用自签名证书“CyberdyneIndustries”,其CA证书已添加到受信任的CA存储中。当我查看证书时,它检查通过。我已经解决了HTTP命名空间管理的问题。看起来WCF并不真正支持这一点,请告诉我我错了。谢谢您。
4个回答

2
最终,我决定尝试消息安全性,看看是否能为这种情况提供一些启示-它确实做到了,并且我将减少损失并采用该方法。因此,这没有明确的答案。
然而,实施消息安全确实暴露了一个很大的问题,这可能是运输安全问题的根源。MSDN上有一份有毒的文档:

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

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

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

参数“signature”应改为“exchange”。一旦重新生成所有证书,消息安全性就开始起作用了。从中可以得出一个重要教训,即如果您想要实现传输安全,请先使消息安全性起作用,因为系统返回的错误信息更加详细。

1
SSL握手成功了吗?启用SChannel日志来排除SSL层的问题。请参考这篇旧的知识库文章:如何在IIS中启用SChannel事件日志记录。虽然这是针对W2K和XP的知识库文章,但是启用SChannel日志的步骤在新系统上仍然适用和有效。启用日志记录后,您将能够确定为什么SSL拒绝该证书。

1
我知道这篇文章已经三年了,但对于那些仍然感兴趣的人...

我正在学习WCF(包括安全性等方面),并且能够使用Transport安全模式和clientCredentialType="Certificate"(以及protectionLevel="EncryptAndSign",尽管这与问题无关)成功地将netTcpBinding(可能也适用于WsHttpBindings)运行正常。

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

以下是我的服务器端配置:

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

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


0

WsHttpBinding支持传输安全的证书认证。

可能有几个问题:

你是否将两个证书都添加到了存储中?CyberdyneIndustries和用于签名的CA?CA 应该在“受信任的根证书颁发机构”中。 此外,我是自托管的,从未在 Visual Studio Dev 服务器上使用过。尝试至少使用 IIS 托管您的服务。我不确定 VS Dev 服务器是否支持证书。 尝试关闭服务身份验证。这样客户端就不必对服务进行身份验证。我不知道你是否想在你的应用程序中使用它,但只是为了测试,以便我们可以排除这个问题。 ``` ```

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接