My particular problem is something like this:
-
We are currently running a set of services which requires the clients to provide a username and password as authentication when calling the services.
-
We would like to implement a PKI-infrastructure on these services, but some of our partners will use longer time to accommodate to this new infrastructure than the others.
-
As a first step we want to require client certificates from some of our partners. A client certificate will be required (in addition to username and password) to access their data on our servers, while for the other users only username and password will be required.
To solve this problem I am trying to implement a custom validator for both the username/password authentication (using UserNamePasswordValidator) and for the client certificates (using X509CertificateValidator) in WCF. The username/password validator will verify these credentials towards our database, while the client certificate validator will inspect whether the request is from a client from which we require a certificate, and if so verify that a valid client certificate is provided. I have not been able to configure WCF so that it uses both of these validators.
My WCF configuration on the server is currently set up like this:
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpsGetEnabled="true" policyVersion="Policy15" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<authentication customCertificateValidatorType="MyWS.Security.MyServicesCertificateValidator, MyWS"
certificateValidationMode="Custom" revocationMode="NoCheck" />
</clientCertificate>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="MyWS.Security.MyServicesUsernameValidator, MyWS" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<bindings>
<basicHttpBinding>
<binding name="MySoapBinding">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Certificate" />
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="MyServiceBehavior" name="MyWS.Services.TheService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="MySoapBinding" name="TheService" bindingNamespace="https://services.my/TheService" contract="MyWS.Interfaces.Service.ITheService" />
<host>
<baseAddresses>
<add baseAddress="https://localhost:4434/MyWS/TheService"/>
</baseAddresses>
</host>
</service>
</services>
As far as I understand this configuration is invalid because I can’t use the customCertificateValidatorType at the transport layer (because IIS inspects the certificate before WCF is involved here), but I can not see how I am able to combine both the customCertificateValidatorType and customUserNamePasswordValidatorType as credential types at the message layer either.
I have implemented a message inspector and might be able to solve the problem using the OperationContext in some way (as suggested in the link below), but I have not been able to see a way for me to do it this way yet.
http://social.msdn.microsoft.com/Forums/en/wcf/thread/b6ab8b58-516b-41d4-bb0e-75b4baf92716
I suppose I might be trying to implement something that is incompatible with the way WCF works, but if someone have an idea about how this could be fixed I would be delighted to have your feedback on this.
I think I have found a solution to my problem now thanks to valuable input from @ladislav-mrnka in his answer. I realized it is necessary to provide two endpoints to configure the different requirements, and I also learned about the supporting token possibilities when configuring the services.
I found a link about supporting tokens at MSDN, and by following this recipe I have implemented the endpoint on the server with the following custom binding (I switched to configuration through code. Not sure if this can be set up in web.config as well.)
This binding creates a SymmetricSecurityBindingElement where a symmetric key (encrypted with the server certificate) is used to encrypt a username/password security token in the message header, and the message body itself.
In addition a X509 security token is added as an endorsing, supporting token to the binding. This token is configured to always be included in the client requests to the server.
This custom binding was subsequently used to configure a new WCF-service with an endpoint requiring this binding. I am using the WcfFacility in Castle Windsor to configure the service.
This code does the following:
MyServicesUsernameValidator inherits UserNamePasswordValidator and MyServicesCertificateValidator inherits X509CertificateValidator. Both overrides their corresponding Validate methods.
This seems to solve my particular problem… Hope it solves yours! 🙂