A while back I hinted that there was another way to get solve the certificate problem when using the Https security mode in Indigo. It turns out that it's possible to register a System.Net.Security.RemoteCertificateValidationCallback event handler on the client to do custom validation of the certificate provided by the service. This gave me the hook I needed to check whether the provided certificate contained the correct DNS name even though the URL for the service specified localhost as the address.
The signature for the event handler is a little daunting;
public delegate bool RemoteCertificateValidationCallback ( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors );
but it turns out to be fairly easy to implement what I'm looking for. Here's the event handler implementation I used;
staticbool ValidateServerCert(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
bool bRet = false;
if (cert.Subject.StartsWith("CN=")) {
if (cert.Subject.Substring(cert.Subject.IndexOf('=') + 1).StartsWith(Dns.GetHostName()))
bRet = true;
}
return bRet;
}
The code above just pulls out the Subject field of the X509 certificate and checks to make sure that the name begins with the hostname of the machine I'm actually running on. Given that I know my URL is localhost, then provided the machine name is actually my machine name, I know I'm good to go.
The rest of the client code looks like this;
staticvoid Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(ValidateServerCert);
ChannelFactory<ISimpleChannel> cf = newChannelFactory<ISimpleChannel>("SecurityBasicEndpointConfig");
ISimpleChannel sc = cf.CreateChannel();
Console.WriteLine("{0}", sc.Ping("Hello, World!"));
}
Note the registration of the event handler with the ServicePointManager. The config file for the client looks like this;
<configurationxmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0" >
<system.serviceModel>
<bindings>
<basicProfileBinding>
<bindingconfigurationName="SecurityBasicClientBinding"securityMode ="Https" />
</basicProfileBinding>
</bindings>
<client>
<endpointconfigurationName="SecurityBasicEndpointConfig"
address="https://localhost:8088/securitybasic"
bindingSectionName="basicProfileBinding"
bindingConfiguration="SecurityBasicClientBinding"
contractType="Gudge.Samples.ISimple, Client" />
</client>
</system.serviceModel>
</configuration>
The server side code looks just like that shown earlier while the the config looks like this;
<configurationxmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0" >
<system.serviceModel>
<bindings>
<basicProfileBinding>
<bindingconfigurationName="SecurityBasicServiceBinding"
securityMode ="Https" />
</basicProfileBinding>
</bindings>
<services>
<serviceserviceType="Gudge.Samples.Service" >
<endpointaddress="https://localhost:8088/securitybasic"
bindingSectionName="basicProfileBinding"
bindingConfiguration="SecurityBasicServiceBinding"
contractType="Gudge.Samples.ISimple, Service" />
</service>
Read more: pluralsight