views:

617

answers:

1

Is there an easy way to tie custom X509 cert validation to BasicHttpBinding (or CustomHttpBinding for the same matter, which will implement transport-only security)?

EDIT1: I added a ServerCertificateValidationCallback to the code for the sake of showing that it doesn't fire up either

Here's what I'm trying to do:

1) wrote custom X509CertificateValidator:

public class MyX509Validator : X509CertificateValidator
{
    public override void Validate(X509Certificate2 certificate)
    {
        Console.WriteLine("Incoming validation: subj={0}, thumb={1}",
                certificate.Subject, certificate.Thumbprint);
    }
}

2) created host:

var soapBinding = new BasicHttpBinding() { Namespace = "http://test.com" };
soapBinding.Security.Mode = BasicHttpSecurityMode.Transport;
soapBinding.Security.Transport.ClientCredentialType =
            HttpClientCredentialType.Certificate;

var sh = new ServiceHost(typeof(Service1), uri);
sh.AddServiceEndpoint(typeof(IService1), soapBinding, "");
sh.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine,
           StoreName.My, X509FindType.FindBySubjectName, "localhost");
sh.Credentials.ClientCertificate.Authentication.CertificateValidationMode =
           System.ServiceModel.Security.X509CertificateValidationMode.Custom;
sh.Credentials.ClientCertificate.Authentication.CustomCertificateValidator = 
           new MyX509Validator();
System.Net.ServicePointManager.ServerCertificateValidationCallback += 
           delegate(object sender, X509Certificate certificate, 
                    X509Chain chain, SslPolicyErrors sslPolicyErrors)
           {
                Console.WriteLine("Incoming validation: subj={0}, thumb={1}",
                    certificate.Subject, certificate.GetIssuerName());
                return true;
           };
sh.Open();

3) Created WCF Client:

var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var cli = new ServiceReference2.Service1Client(binding, 
           new EndpointAddress("https://localhost:801/Service1"));
cli.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, 
           StoreName.My, X509FindType.FindBySubjectName, "localhost");
cli.HelloWorld();

Authentication works fine, but MyX509Validator.Validate() never gets called. I have suspicion that X509CertificateValidator only works on message security, not on transport. Is that right? Is there something I could do to override transport-level cert validation?

+1  A: 

I'm assuming that you are talking about the certificate that is tied to HTTPS...

If so, here is the answer: The X509CertificateValidationMode is used within the header of the document that you are exchanging to provide authentication. It does nothing for the underlying transport itself, which in this case is HTTPS with its' associated x.509 cert. So, you are spot on with your assumption.

If you want to provide custom certificate validation of the transport, use System.Net.ServicePointManager.ServerCertificateValidationCallback instead. Be careful though, this is an app wide setting. If you are cycling through multiple endpoints and don't set this to null, it will remain active.

Mitch Baker
thanks for answer! It almost was a home run, except ServerCertificateValidationCallback apparently only validates certificate from server, not client :( . But it was a great idea, you got my upvote
galets
Swing and a miss. I'll keep thinking about it and post if anything seems to make sense. One thing I didn't think about was you are on the hosting side. hmmmm...
Mitch Baker
Are you getting an exception of some sort? Is the exception on the client or server side?
Mitch Baker
When certificate is kosher, no exception. When cert is unknown, server rejects client certificate with 404. In both cases, validator isn't getting called on server side. However, I added same code to client and it got triggered with server certificate in it.
galets