views:

227

answers:

0

I'm currently developing a RESTful service using WCF and WCF Rest Contrib. The service is split into two parts: BasicAuthService and CertAuthService. On the first one the client is authenticated using Basic authentication (over HTTPS) and on the second X509 client certificates are used.

My problem is that IIS never requests a client certificate from the client when SslRequireCert is set on the path (which it is for ClientAuthService). If I set it to SslNegotiateCert then IIS sends a certificate request during the TLS-handshake but then it will let the client through if no certificate is returned by the client (which is how SslNegotiateCert should work). If I set SslNegotiateCert on some other path and then the client requests that path first and then ClientAuthService, then the client will authenticate itself using the certificate; because then the TLS-session is cached from the previous request.

My question is: do you know any way to solve this? The only idea I have right now is to set SslNegotiateCert on CertAuthService and then implement some code myself which checks for a valid certificate and returns 403 otherwise. However I'd prefer to not have to implement security checks like these myself because there's always a risk that you screw up when you write your own "security-code".

You can see the web.config I'm using below (it isn't a complete web.config, I've removed irrelevant parts).

<configuration>
  <system.web>
 <httpModules>
   <add name="ServiceAnonymityModule" type="WcfRestContrib.Web.ServiceAnonymityModule, WcfRestContrib" />
 </httpModules>
  </system.web>
  <location path="CertAuthService.svc">
 <system.webServer>
   <security>
  <access sslFlags="SslRequireCert" />
   </security>
 </system.webServer>
  </location>
  <system.webServer>
 <security>
   <access sslFlags="SslNegotiateCert" />
 </security>
 <validation validateIntegratedModeConfiguration="false" />
 <modules>
   <remove name="ServiceAnonymityModule" />
   <add name="ServiceAnonymityModule" type="WcfRestContrib.Web.ServiceAnonymityModule, WcfRestContrib" />
 </modules>
 <handlers>
   <remove name="WebServiceHandlerFactory-Integrated" />
 </handlers>
  </system.webServer>
  <system.serviceModel>
 <services>
   <service name="RestWeb.BasicAuthService" behaviorConfiguration="RestWeb.BasicAuthServiceBehaviour">
  <endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBindingBasicConfig" contract="RestWeb.BasicAuthService">
  </endpoint>
   </service>
   <service name="RestWeb.CertAuthService" behaviorConfiguration="RestWeb.CertAuthServiceBehaviour">
  <endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBindingCertConfig" contract="RestWeb.CertAuthService">
  </endpoint>
   </service>
 </services>
 <extensions>
   <behaviorExtensions>
  <add name="webAuthentication" type="WcfRestContrib.ServiceModel.Configuration.WebAuthentication.ConfigurationBehaviorElement, WcfRestContrib, Version=1.0.5.0, Culture=neutral, PublicKeyToken=89183999a8dc93b5" />
  <add name="errorHandler" type="WcfRestContrib.ServiceModel.Configuration.ErrorHandler.BehaviorElement, WcfRestContrib, Version=1.0.5.0, Culture=neutral, PublicKeyToken=89183999a8dc93b5" />
  <add name="webFormatter" type="WcfRestContrib.ServiceModel.Configuration.WebDispatchFormatter.ConfigurationBehaviorElement, WcfRestContrib, Version=1.0.5.0, Culture=neutral, PublicKeyToken=89183999a8dc93b5" />
  <add name="webErrorHandler" type="WcfRestContrib.ServiceModel.Configuration.WebErrorHandler.ConfigurationBehaviorElement, WcfRestContrib, Version=1.0.5.0, Culture=neutral, PublicKeyToken=89183999a8dc93b5" />
   </behaviorExtensions>
 </extensions>
 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
 <bindings>
   <webHttpBinding>
  <binding name="webHttpBindingBasicConfig">
    <security mode="Transport">
   <transport clientCredentialType="None" />
    </security>
  </binding>
  <binding name="webHttpBindingCertConfig">
    <security mode="Transport">
   <transport clientCredentialType="Certificate" />
    </security>
  </binding>
   </webHttpBinding>
 </bindings>
 <behaviors>
   <serviceBehaviors>
  <behavior name="RestWeb.BasicAuthServiceBehaviour">
    <serviceDebug includeExceptionDetailInFaults="true" />
    <serviceCredentials>
    </serviceCredentials>
    <webFormatter>
   <formatters defaultMimeType="application/octet-stream">
     <formatter mimeTypes="application/octet-stream" type="RestWeb.Serialization.ProtocolBuffersEncoder, RestWeb" />
   </formatters>
    </webFormatter>
    <errorHandler errorHandlerType="WcfRestContrib.ServiceModel.Web.WebErrorHandler, WcfRestContrib" />
  </behavior>
  <behavior name="RestWeb.CertAuthServiceBehaviour">
    <serviceDebug includeExceptionDetailInFaults="true" />
    <serviceMetadata httpsGetEnabled="true" />
    <serviceCredentials>
    </serviceCredentials>
    <webFormatter>
   <formatters defaultMimeType="application/octet-stream">
     <formatter mimeTypes="application/octet-stream" type="RestWeb.Serialization.ProtocolBuffersEncoder, RestWeb" />
   </formatters>
    </webFormatter>
    <errorHandler errorHandlerType="WcfRestContrib.ServiceModel.Web.WebErrorHandler, WcfRestContrib" />
  </behavior>
   </serviceBehaviors>
 </behaviors>
  </system.serviceModel>
</configuration>