I have a simple C# application that talks to a remote server over TLS. When I attempt to "AuthenticateAsClient," I immediately receive the dreaded "the client and the server cannot communicated, because they do not possess a common algorithm."
So, I broke out a wire trace and observe that the "Client Hello" completes and serves up a cipher suite list. The "Server Hello" completes with a certificate and a selected cipher suite from the list provided in the "Client Hello". However, I then expect to see a "CIPHER CHANGE SPEC" next, but instead, the client resets/acks the connection, and fails, and I have no idea why. The server is sending down TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xC014).
What I've tried:
I suspected a problem with the server certificate, but verified that it has a KeySpec of 1 and validates properly. Its cert chain validates. Even when exported from the server and moved to my client box (sans private key), the validation chain seems OK.
I've verified via the IISCrypto tool that AES_256 is enabled and available on both the server and the client. I do notice, however, that in spite of my specifying TLS 1.2, the wire traces show that the server is only responding with TLS 1.0. I pointed the client to another instance of the application on a different machine, but it does respond with TLS 1.2, and works correctly - but with a different cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xC028).
I've also tried the opposite extreme of forcing the app down to TLS 1.0 against the "good" (working) server, and the result is the same - it still works.
I would assume that the failure to send the CIPHER CHANGE SPEC was due to the presumably incorrect determination that it had no common algorithms, but how could the client have offered it if it weren't available? Can something be "corrupted" within one of those cipher specs that wouldn't be realized until it was actually used?
For completeness, I've excerpted the portion of the program that does the SSL connect into a small console app, and it seems trivially simple, but I've reproduced it here in the event someone can see that I'm doing something incorrectly:
static void Main(string[] args)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
int portNumber = 9999999; //edited for obvious reasons
TcpClient x = new TcpClient("the.server.name", portNumber);
var foo = new System.Net.Security.SslStream(x.GetStream(), false,
(a,b,c,d) => {
Console.WriteLine("connected");
return true;
});
foo.AuthenticateAsClient("the.server.name");
Console.WriteLine(foo.CipherAlgorithm.ToString());
}