There are a couple of surprises you can encounter on Windows/.net when your web application makes an SSL connection to a SOAP service.
The typical symptom is that you can connect fine using SoapUI or a similar testing tool, but the web application itself fails with a fairly generic exception about being unable to establish the SSL connection.
The first rule in such situations is to dive into the Windows Event Viewer to see what's going on. And in this case you soon see the smoking gun - in the System log will be an error from Schannel. However, the kind of error you see may not be much more helpful, being something along the lines of:
"A fatal error occurred when attempting to access the SSL client credential private key. The error code returned from the cryptographic module is 0x8009030d. The internal error state is 10003."
"The following fatal alert was received: 40."
Let's look at the first one, because at least it's trying to give us some detail. We saw this happen when trying to authenticate the connection with an intermediate certificate. Why it works in SoapUI but not in the .net application is actually quite simple: it's a permission issue. The interactive user has permission to load the certificate, but web apps running in IIS don't use this account; they use one configured within IIS.
There are two ways round this. One is to change the application pool user in IIS to an account with higher privileges - but that's got some security implications, so we shouldn't do that if we can avoid it. The better is to make sure the application pool user can access the certificate. Some good guidance in this StackOverflow question: http://stackoverflow.com/questions/4945687/how-to-grant-an-account-permissions-to-access-a-certificate
In our situation, that was necessary to get things working, but not sufficient. We still had that mysterious "error 40" going on.
A session in Wireshark gave away the secret there. Error 40 refers to a failure in the SSL handshake: the process by which the client and server decide on an encryption method. In our situation, it was because the client was trying to negotiate an SSLv3 connection - and the server, in a post-POODLE world was rightfully closing the connection, refusing to accept an insecure transmission method.
This is where an extremely useful class in .net comes to the rescue - System.Net.ServicePointManager. It allows us to specify explicitly a lot of the behaviour around creating and negotiating SSL connections, and authenticating certificates. In our case, the SecurityProtocolType property is the part we want, allowing us to specify that we want to use TLS rather than SSLv3. Once this is set the final piece falls into place: the web application has permissions to load the intermediate certificate it needs, and it negotiates a protocol the server is happy to work with.