Answered by:
wcf certificate authentication with custom certificate validator and basicHttpBindings

Question
-
Hello
I've read multiple posts relating to this issue but none do exactly what we're trying to do and I'm not sure if what we'd like to do is possible or not.
We're trying to set up a wcf service using certificate authentication. We will be calling the service over https so we don't require the message to be encrypted. We are therefore using basicHttpBinding and TransportWithMessageCredential. Although for testing I'm using TransportCredentialOnly.
In order to attach the certificate to the message I have set up the ProtectionLevel=System.Net.Security.ProtectionLevel.Sign attribute on the wcf interface ServiceContractAttribute tab.
At the service we would like to validate the certificate ourselves to I've created my own certificate validator derived from X509CertificateValidator and overridden the Validate method.
When testing the service in the development environment I'm finding that the custom validator is not being called and when looking the the request in Fiddler the header is not including the certificate.
These are the configuration settings I have set up on the service side in my development environment.
<services>
<service behaviorConfiguration="CertificateBehavior" name="B2B_ReceivingProviderUnipass">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="BasicHttpBindingWithCertificate"
contract="MyContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="CertificateBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceCredentials>
<clientCertificate>
<authentication revocationMode="NoCheck" certificateValidationMode="Custom" customCertificateValidatorType="Namespace.CertificateValidator, Assembly" />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBindingWithCertificate">
<security mode="TransportCredentialOnly">
<message clientCredentialType="Certificate" algorithmSuite="Basic128" />
</security>
</binding>
</basicHttpBinding>
</bindings>And this is what I have on the client side.
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBindingWithCertificate">
<security mode="TransportCredentialOnly">
<message clientCredentialType="Certificate" algorithmSuite="Basic128" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="url"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBindingWithCertificate"
contract="MyContract"
name="BasicHttpBindingWithCertificate" />
</client>
</system.serviceModel>I'm using client.ClientCredentials.ClientCertificate.SetCertificate in my test to add the certificate to the message. And before calling the service I can see that the certificate is definitely set in the ClientCredentials, ClientCertificate property.
I'd be very grateful to find out what it is that I'm doing wrong.
Thanks.
Anne
Wednesday, August 10, 2011 8:50 AM
Answers
-
Currently the error you are getting in the client is a general error, because your service isn't configured to return inner exceptions to the client (to do that you need to add the serviceDebug behavior to the service behavior and set the includeExceptionDetailInFaults attribute).
Since you are getting some sort of an exception on the service side, I suggest you first turn on WCF tracing on the service side and check the logs to see which exception you are getting.
Please mark posts as answers/helpful if it answers your question.
Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper.
Visit my blog: http://blogs.microsoft.co.il/blogs/idof- Proposed as answer by Ido Flatow. _ Wednesday, August 10, 2011 7:45 PM
- Marked as answer by Yi-Lun Luo Thursday, August 11, 2011 1:58 AM
Wednesday, August 10, 2011 1:55 PM
All replies
-
You use TransportCredentialsOnly means that the credentials are passed in the http headers, not in the message, so the message security settings you did are not affective.
Change it to TransportWithMessageCredentials to make it work.
Please mark posts as answers/helpful if it answers your question.
Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper.
Visit my blog: http://blogs.microsoft.co.il/blogs/idof- Proposed as answer by Ido Flatow. _ Wednesday, August 10, 2011 7:45 PM
Wednesday, August 10, 2011 9:01 AM -
Thanks. I'm trying to get this working in my development environment first so I'm not using https. I thought that TransportWithMessageCredentials required https.Wednesday, August 10, 2011 12:01 PM
-
It does require https. If you use transport credentials only than you need to use the <transport> section instead of the <message> section, and you won't be able to use the custom validator, because the certificate will be check by the operating system, not by WCF (to override it you will need to supply a delegate to the ServicePointManager class in System.Net).
It is preferable that you run your code as it will be used in the server, so you'll be able to test everything.
You can use self-signed certificates if you don't have a purchased cert.
Please mark posts as answers/helpful if it answers your question.
Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper.
Visit my blog: http://blogs.microsoft.co.il/blogs/idofWednesday, August 10, 2011 12:08 PM -
Thanks Ido. I've tried setting things up in the test environment but again I'm having problems.
Can I run through what I've set up and could you please let me know where I'm going wrong.
Server side config file:
<service behaviorConfiguration="CertificateBehavior" name="B2B_ReceivingProviderUnipass">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="httpsBindingWithUnipass"
contract="Origo.Services.B2B.Options.External.ReceivingProvider.ReceivingProviderServiceType"
behaviorConfiguration="ReceivingProviderEndPointBehavior">
</endpoint>
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
<behavior name="CertificateBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceCredentials>
<clientCertificate>
<authentication revocationMode="NoCheck" certificateValidationMode="Custom" customCertificateValidatorType="Origo.Services.B2B.Options.UserValidation.CertificateValidator, Origo.Services.B2B.Options.UserValidation" />
</clientCertificate>
</serviceCredentials>
</behavior>
<basicHttpBinding>
<binding name="httpsBindingWithUnipass">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" algorithmSuite="Basic128" />
</security>
</binding>
</basicHttpBinding>I've added the following to the service interface:
[System.ServiceModel.ServiceContractAttribute(Namespace="http://www.origoservices.com/schema/OptionsIntegration/v1.0/ReceivingProviderService", ProtectionLevel=System.Net.Security.ProtectionLevel.Sign)]
On the server I've set up the service to require SSL and accept certificates. I can browse to the service without error.
On the client side:
I've set up a test project and created a service reference to the service which has automatically created the following in the app.config file.
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ReceivingProviderServiceType"
closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00"
sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false"
hostNameComparisonMode="StrongWildcard" maxBufferSize="65536"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Certificate" algorithmSuite="Basic128" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="service url"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ReceivingProviderServiceType"
contract="ReceivingProviderUnipassProxy.ReceivingProviderServiceType"
name="BasicHttpBinding_ReceivingProviderServiceType" />
</client>
</system.serviceModel>
In my code before I call the service I've set the following:client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "Name");
When I debug through I get the following error:
An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.
An error occurred when processing the security tokens in the message.Anne
Wednesday, August 10, 2011 1:18 PM -
Currently the error you are getting in the client is a general error, because your service isn't configured to return inner exceptions to the client (to do that you need to add the serviceDebug behavior to the service behavior and set the includeExceptionDetailInFaults attribute).
Since you are getting some sort of an exception on the service side, I suggest you first turn on WCF tracing on the service side and check the logs to see which exception you are getting.
Please mark posts as answers/helpful if it answers your question.
Senior Consultant on WCF, ASP.NET, Siverlight, and Entity Framework. Author of Microsoft's Official WCF 4 Course. Co-author of the Microsoft HPC/Azure burst whitepaper.
Visit my blog: http://blogs.microsoft.co.il/blogs/idof- Proposed as answer by Ido Flatow. _ Wednesday, August 10, 2011 7:45 PM
- Marked as answer by Yi-Lun Luo Thursday, August 11, 2011 1:58 AM
Wednesday, August 10, 2011 1:55 PM -
Thanks Ido.
It's working. I set up the tracing as you suggested, ploughed through the log file and it was failing in the custom certificate validator quite correctly cause I hadn't set my certifcate details up in the database it's using to authenticate against. I'll need to work on getting the specific error it raised returned in a more meaningful way. But having set the certificate details up I can now get the service to run through successfully.
Thanks for your help.
Anne
Wednesday, August 10, 2011 4:06 PM -
Thanks Ido.
It's working. I set up the tracing as you suggested, ploughed through the log file and it was failing in the custom certificate validator quite correctly cause I hadn't set my certifcate details up in the database it's using to authenticate against. I'll need to work on getting the specific error it raised returned in a more meaningful way. But having set the certificate details up I can now get the service to run through successfully.
Thanks for your help.
Anne
Wednesday, August 10, 2011 4:06 PM