简体   繁体   中英

WCF Service Using Client Certificates Requires Anonymous Access in IIS and Therefore Doesn't Actually Work?

I have a WCF service (.NET 4) hosted in IIS (7.5 on Windows Server 2008 R2). Our customer (for whom we are building the service) requires that all clients of the service be authenticated using certificates from Verisign. That is, the client purchases a certificate from Verisign and provides us with the public key. The service should only accept requests that can be validated using one of the public keys that we have received from the clients.

The problem that I am having is that IIS seems to require that Anonymous authentication be enabled. If it isn't, a "Security settings for this service require 'Anonymous' Authentication but it is not enabled for the IIS application that hosts this service." error is raised. However, if I enable anonymous authentication, any call to the service that provides any certificate is allowed. We need to restrict it to only allow calls that provide specific certificates (ie ones for which we have been supplied the public key).

In IIS we require SSL and client certificates (under SSL Settings), and (under Authentication) all authentication is disabled (except for anonymous).

The web.config is:

  <system.web>
    <compilation debug="false" targetFramework="4.0" />
    <authentication mode="Windows" />
    <authorization>
      <deny users="?" />
      <allow users="*" />
    </authorization>
    <customErrors mode="Off" />
    <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />
    <identity impersonate="false" />
  </system.web>

  <system.serviceModel>
    <services>
      <service behaviorConfiguration="XXXServiceBehavior" name="XXXService.NewService">
        <endpoint address=""
                  binding="wsHttpBinding"
                  bindingConfiguration="XXXServiceBinding"
                  contract="XXXService.INewService"
                  bindingNamespace="http://XXXX.com.au/XXXService/NewService">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="XXXXServiceBehavior">
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <clientCertificate>
              <authentication certificateValidationMode="PeerTrust" mapClientCertificateToWindowsAccount="true" />
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="XXXServiceBinding" maxReceivedMessageSize="1000000" maxBufferPoolSize="1000000">
          <readerQuotas maxStringContentLength="1000000" />
          <security mode="Transport">
            <transport clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
  </system.serviceModel>

If anyone is able to get this sort of scenario working, could you please let me know what settings you are using in IIS and in your configuration?

It turns out that what I needed was to use a binding security mode of TransportWithMessageCredential , and a message clientCredentialType of Certificate (see the full config listing below).

With this binding configuration I don't need to demand the principal permissions or any mapping of certificates to Windows accounts, since the service won't get executed if the caller does not provide a recognized certificate (ie a certificate whose public key has been imported into the 'Trusted People' store on the server, at the machine level).

The only other setting change is in IIS and the SSL Settings. I had to change the 'Client certificate' setting from 'Require' to 'Accept'. (Leaving it as 'Require' results in a "The HTTP request was forbidden with client authentication scheme 'Anonymous'" error being received by the client.) I'm not sure why this is the case, but I don't think this is a problem since WCF will require the certificate.

So here's the relevant config. This is the server config:

<system.web>
  <compilation debug="false" targetFramework="4.0" />
  <authentication mode="Windows" />
  <authorization>
    <deny users="?" />
    <allow users="*" />
  </authorization>
  <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />
  <identity impersonate="false" />
</system.web>

<system.serviceModel>
  <services>
    <service behaviorConfiguration="XXXWebServiceBehavior" name="XXXWebService.NewService">
      <endpoint address="" binding="wsHttpBinding" bindingConfiguration="XXXWebServiceBinding" contract="XXXWebService.INewService" />
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="XXXWebServiceBehavior">
        <serviceDebug includeExceptionDetailInFaults="false" />
        <serviceCredentials>
          <clientCertificate>
            <authentication certificateValidationMode="PeerTrust" />
          </clientCertificate>
        </serviceCredentials>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <bindings>
    <wsHttpBinding>
      <binding name="XXXWebServiceBinding" maxReceivedMessageSize="1000000" maxBufferPoolSize="1000000">
        <readerQuotas maxStringContentLength="1000000" />
        <security mode="TransportWithMessageCredential">
          <message clientCredentialType="Certificate" />
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>
</system.serviceModel>

And here's the client config (mostly generated by Visual Studio):

<system.serviceModel>
  <bindings>
    <wsHttpBinding>
      <binding name="WSHttpBinding_INewService" 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">
          <message clientCredentialType="Certificate" />
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>
  <client>
    <endpoint address="https://XXXX.XXXX.com.au/XXXServices/NewService.svc"
              binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_INewService"
              contract="ServiceReference1.INewService" name="WSHttpBinding_INewService">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>
  </client>
</system.serviceModel>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM