WCF 4 - 使用X.509证书进行传输和消息安全的TransportWithMessageCredential

时间:2011-10-01 14:24:41

标签: wcf ssl x509certificate ws-security transport-security

我正在尝试在IIS上托管WCF4服务,该服务将使用X.509证书进行邮件安全,并使用SSL进行相互身份验证以确保传输安全性(项目规范要求使用WS-Security AND SSL,AFAIK只有一个通常是要走的路,但没关系)。

我制作了TestRootCA并用它来颁发服务器证书(localhost)和客户端证书(TestUser)。我首先想要建立和测试传输安全性,因此我将IIS配置为使用https,配置的SSL证书(我制作的localhost证书),并将IIS配置为要求来自客户端的客户端证书。然后我修改了服务web.config,制作了相应的svcutil.conf并运行了svcutil,成功地为我的测试WinForms应用程序创建了客户端代理类和app.config。我通过调用:

创建代理客户端时设置证书
var client = new ServiceClient();
client.ClientCredentials.ClientCertificate.SetCertificate(System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser, System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectDistinguishedName, "CN=TestUser");
MessageBox.Show(client.GetData(42));

到目前为止一切顺利。但是,当我修改服务的web.config以使用TrasportWithMessageCredential(而不是Transport)并指定“Certificate”作为messageCredentialType用于消息安全时,我得到HTTP 403 - The HTTP request was forbidden with client authentication scheme 'Anonymous',即使我指定了“证书”。

我的最终web.config看起来像这样:

<system.serviceModel>
    <bindings>
        <wsHttpBinding>
            <binding name="wsHttpEndpointBinding">
                <security mode="TransportWithMessageCredential">
                    <transport clientCredentialType="Certificate"/>
                    <message clientCredentialType="Certificate"/>
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>
    <services>
        <service behaviorConfiguration="serviceBehavior" name="Service">
            <endpoint binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="wsHttpEndpoint" contract="IService" />
            <endpoint address="mex" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="mexEndpoint" contract="IMetadataExchange" />
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="serviceBehavior">
                <serviceMetadata httpsGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

和我的客户端app.conf:

<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="TransportWithMessageCredential">
                    <transport clientCredentialType="Certificate"/>
                    <message clientCredentialType="Certificate"/>
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>
    <client>
        <endpoint address="https://localhost/WCFTestService/Service.svc" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpoint" contract="IService" name="wsHttpEndpoint" />
    </client>
</system.serviceModel>

有什么想法吗?

编辑:

我已成功通过将客户端证书的IIS SSL设置从要求更改为接受来使其正常工作。似乎当我使用SSL通过传输安全性使用证书引入消息安全性时,客户端使用证书进行消息签名,但用于通过SSL进行相互身份验证(它尝试以匿名身份登录< / em>即使它已分配证书)。

不确定为什么会发生这种情况,我想要专家的一些评论:)。

2 个答案:

答案 0 :(得分:2)

正如问题中所述,我通过将客户端证书的IIS SSL设置从require更改为accept来实现它。然后在服务入口点我编程检查远程用户的证书(如果它不为空且有效)。

答案 1 :(得分:1)

您必须在行为部分中指定用于加密邮件的证书,因为这些证书可能与用于建立https渠道的证书不同

它在服务器中看起来像这样

<system.serviceModel> 
 <behaviors>     
    <serviceBehaviors>     
        <behavior name="serviceBehavior">
      <serviceCredentials>
        <serviceCertificate findValue="ServerCertificate"
                            storeLocation="CurrentUser"
                            storeName="My"
                            x509FindType="FindByIssuerName" />
        <clientCertificate>
          <certificate findValue ="ClientCertificate"
                       storeLocation="CurrentUser"
                            storeName="My"
                            x509FindType="FindByIssuerName"/>
          <authentication certificateValidationMode ="PeerTrust"/>
        </clientCertificate>
      </serviceCredentials>
            <serviceMetadata httpsGetEnabled="true" />     
            <serviceDebug includeExceptionDetailInFaults="true" />     
        </behavior>     
    </serviceBehaviors>     
</behaviors>   
</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="TransportWithMessageCredential">        
                    <transport clientCredentialType="Certificate"/>        
                    <message clientCredentialType="Certificate"/>        
                </security>        
            </binding>        
        </wsHttpBinding>        
    </bindings>
 <behaviors>      
  <endpointBehaviors>        
    <behavior name="ClientCredentialsBehavior">
      <clientCredentials>            
        <clientCertificate x509FindType="FindBySubjectName"
                           findValue="ClientCertificate"
                           storeLocation="CurrentUser"
                           storeName="My"/>
        <serviceCertificate>
          <defaultCertificate x509FindType="FindBySubjectName"
                              findValue="ServerCertificate"
                              storeLocation="CurrentUser"
                              storeName="My"/>
        </serviceCertificate>
      </clientCredentials>
    </behavior>
  </endpointBehaviors>
</behaviors>

    <client>        
        <endpoint address="https://localhost/WCFTestService/Service.svc" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpoint" contract="IService" name="wsHttpEndpoint" behaviorConfiguration="ClientCredentialsBehavior" />        
    </client>        
</system.serviceModel>

当然,您必须在服务器和客户端中设置正确的证书位置和名称。

我希望这仍然有帮助