I've started looking at the various security options for the Indigo services and I decided to try plain HTTPS with the basicProfileBinding as a first stab. Once more I took a config based approach. It took me a while to get it working and I did need some help from some of the other folks on the Indigo security team to figure out the exact set of steps necessary to get SSL set up correctly.
The code for the
client and
service are as shown in my earlier entries. The config file for the service now looks like this;
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0" >
<system.serviceModel>
<bindings>
<basicProfileBinding>
<bindingconfigurationName="SecurityBasicServiceBinding"
securityMode ="Https" />
</basicProfileBinding>
</bindings>
<services>
<serviceserviceType="Gudge.Samples.Service" >
bindingSectionName="basicProfileBinding"
bindingConfiguration="SecurityBasicServiceBinding"
contractType="Gudge.Samples.ISimple, Service" />
</service>
</services>
</system.serviceModel>
</configuration>
The securityMode attribute on the binding element, along with the https URI in the address attribute on the endpoint element configure the service to use SSL. If these don't match ( e.g. http URI for address with Https securityMode ) then the service will throw an exception at Open time. The exception message is reasonably informative; "The provided URI scheme 'http' is invalid; expected 'https'."
The above config is not quite enough to configure the service completely, I also need to specify the certificate to identity the server in SSL exchange. I did this with httpcfg.exe, which can be used, amongst other things to tell http.sys which certificates to associate with which URIs. In this case the command line I used was;
httpcfg add ssl -i 0.0.0.0:8088 -h abcdefabcdefabcdefabcdef
The add argument tells httpcfg that I'm adding a new mapping. The ssl argument indicates that the mapping I'm adding is an SSL certificate to URI mapping. The -i and following argument indicate the IP address and port number, the 0.0.0.0 indicates my local IP address ( it actually gets assigned by DHCP on my network, so I can't specify a fixed address). The -h and following argument indicate the thumbprint of the certificate to use. A couple of notes about that;
- The certificate needs to be in the LocalMachine store (not CurrentUser)
- The thumbprint is the SHA1 thumbprint, not the MD5 thumbprint.
I actually used my machine certificate, which is my case gets set up when I first pave my machine inside the big house. ( and no, gudgemachine is NOT my actual machine name neither is the above the real SHA1 of my cert ).
So that's the service configured, the client config looks like this;
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0" >
<system.serviceModel>
<bindings>
<basicProfileBinding>
<bindingconfigurationName="SecurityBasicClientBinding"
securityMode ="Https" />
</basicProfileBinding>
</bindings>
<client>
<endpointconfigurationName="SecurityBasicEndpointConfig"
bindingSectionName="basicProfileBinding"
bindingConfiguration="SecurityBasicClientBinding"
contractType="Gudge.Samples.ISimple, Client" />
</client>
</system.serviceModel>
</configuration>
The securityMode attribute on the binding element and the address attribute on the endpoint element perform the same function here as they did with the service (and you'll get the same exception if they don't match up).
With the above config files, httpcfg command line and an appropriate certificate you can successfully secure the communication between client and service using SSL.
As I indicated earlier, it wasn't actually a smooth path to this point of SSL nirvana; for my first attempt I tried to use localhost in the address URI (e.g. https://localhost:8088/securitybasic ) along with a certificate for localhost generated by makecert. It turns out that because certificates generated using makecert don't have a valid root issuer, the certificate fails SSL certificate checking on the client side. Unfortunately the exception you get at this point is not terribly informative; "The underlying connection was closed: An unexpected error occurred on a send." and on digging a little deeper into the exception chain; "Authentication failed because the remote party has closed the transport stream." neither of which gives much clue as to what the problem actually is. Having spoken to some of the folks here on the Indigo security team, I was faced with getting hold of a 'real' certificate with a valid trust root or using such a cert that I already had.
This led me to my next attempt which was to use my machine certificate but still use the localhost URI. This doesn't work either, because the DNS name in the certificate and that in the endpoint URI need to match. As my certificate, not unsurprisingly, does not contain the domain name 'localhost', they don't match. The exception that gets thrown here is somewhat helpful; "Could not establish secure channel for SSL/TLS with authority 'localhost:8088'." and again, on digging deeper; "The remote certificate is invalid according to the validation procedure." OK, so it's not quite as clear as "DNS name in server side certificate doesn't match URI of service" but it's close enough for government work, as they say.
So, make sure you use a 'proper' certificate, and make sure the domain name in that certificate matches the domain name you specify in the service endpoint.
It turns out that those exception messages do give some clue to another mechanism for addressing the 'localhost' problem, but that's the subject of another entry…
Posted
Jun 01 2005, 10:13 AM
by
martin-gudgin