When calling AuthenticateAsClient(), I receive the error "Call to SSPI failed." with the inner exception "The message received was unexpected or badly formatted".
I've seen this to be a semi-popular problem, but I haven't been able to find a working solution. Here is where I'm at after scouring articles for a few hours:
Using my private key, the server issued me a signed client cert
Using open ssl, I combined the two into a pfx using:
openssl pkcs12 -in My-Client-Cert.pem -inkey ssl-client-privatekey.pem -export -out private-key-pair.pfx
I imported the cert to the personal folder on LocalMachine. I was able to see that cert recognized that a private key was present.
The private key is not password protected
I imported the CA Root for the service I'm talking to into both my Trusted Root Certification Authorities and Personal stores.
In app code, I pull the cert by thumbnail. I can see in the debugger that both private and public keys are present.
I initialize a TcpClient
I then initialize an SslStream using the the TcpClient
I call AuthenticateAsClient()
I receive the error message
For the hostname parameter of AuthenticateAsClient() I have also tried using the CN of the CA root and also the CN of the client cert with the same result.
I verified that the service I'm trying to talk to does work just fine when I connect to it using openssl s_client connect
Here is the code for the connection:
private void Setup(string hostname, int port, X509Certificate2Collection certs)
{
try
{
// create the socket
var clientSocket = new TcpClient(hostname, port);
_sslStream = new SslStream(
clientSocket.GetStream(),
false,
ValidateServerCertificate,
null);
_sslStream.AuthenticateAsClient(
"VirtuCrypt Root CA - Test", // hostname here must match the name on the server certificate
certs,
SslProtocols.Tls11,
false);
Debug.WriteLine("Connected!");
}
catch(Exception ex)
{
Debug.WriteLine(ex.Message);
if(ex.InnerException != null)
Debug.WriteLine(ex.InnerException.Message);
}
}
Pulling my hair out... any thoughts?
UPDATE:
I spun up Wireshark and noticed that all communication to and from the host was done over TCP, and none of it was done over TLS. Not sure if that's something I should be concerned with despite the fact that I am requesting TLS12
UPDATE2:
I'm looking at Wireshark again and I fixed the decode so that it shows the TLS communications in addition to the TCP connection. Now that I can see the handshake, I can see that no client cert is actually being presented. I'm providing that cert to SslStream, so I don't know why it's not being transmitted.
Ok after finally making friends with Wireshark and breaking about 4 coffee mugs chasing my tail, here's what I found out...
The CA root that was given to us was a pem file that actually included the CA root and 3 intermediate certs.
I had to break those into separate files and import the CA root into our trusted root CA store and the 3 intermediate certs into the intermediate CA store
I hadn't noticed before, but our client cert that was installed in the personal store was saying it didn't have enough info to be validated, but what was really going on is that it was failing validation because we didn't have the CA certs installed correctly as described above. Once I installed those separately, this message went away
It appears that .Net will not send your client cert unless it can be validated. In my 2nd edit in the OP, I mentioned that I could see the client cert was not being sent at all. This was the actual problem. Once I installed these CA certs, the cert started getting sent and everything else was good.
Related
I am new with MQTT protocol. When I searched for an MQTT server I found that Mosquitto broker is one of the most used one and therefore I have started using it. I have to develop an MQTT client on C#/.NET and I found only the M2Mqtt project and this C# MQTT client example.
I managed to install Mosquitto broker on Windows 10 and change the access control list using topics. Using MqttClient I am able to connect to the broker with an username, subscribe to topics and publish them with the following code.
Connect:
byte result = this.mqttClient.Connect(Guid.NewGuid().ToString(), username, string.Empty);
Subscribe:
this.mqttClient.Subscribe(new string[] { topic }, new byte[] { 2 });
Publish:
ushort result = this.mqttClient.Publish(topic, message, MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, true);
Now I want to add security in communications. I followed these steps to create the CA certificate, the server key and server certificate (I created the certificates twice). I changed the mosquito.conf file as explained in the link:
port 8883
cafile C:\mosquitto\certs\ca.crt
certfile C:\mosquitto\certs\server.crt
keyfile C:\mosquitto\certs\server.key
I don't know if it is necessary, but I added the ca.crt to the Trusted Root Certificates following these steps.
I changed the client to use the CA certificate in the connection:
//this.mqttClient = new MqttClient(brokerAddress);
X509Certificate caCertificate = new X509Certificate("ca.crt");
this.mqttClient = new MqttClient(brokerAddress, 8883, true, caCertificate, null, MqttSslProtocols.TLSv1_0);
A copy of the ca.crt file is in the same folder of the .exe file. When I run the application I always get the same exception:
uPLibrary.Networking.M2Mqtt.Exceptions.MqttConnectionException: Exception connecting to the broker
[Inner exception] System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
Do you know if I am missing something?
managed to get this working.
Instead of using a ca.crt I exported the certificate to a ca.pfx, installed the .pfx certificate in the client computer's Trusted Root Certification Authorities cache.
To install, Simply right click on the file, choose Local machine, and complete prompts as required. Critical to select local machine and select the proper certificate store ("Trusted Root Certification Authorities").
I'm creating a TCP proxy with C# using TcpListener for the proxy server and TcpCLient for the communication between client and proxy and between proxy and target server. This works really nice.
I also have to support SSL and TLS encrypted communication.
This works almost well. I create a SslStream from the proxy to the target server with this Code:
var sslStream = new SslStream(remoteStream, false);
sslStream.AuthenticateAsClient(state.RemoteHost);
And I create a SslStream from the proxy to the Client with the following code:
var sslStream = new SslStream(state.ClientStream, false);
sslStream.AuthenticateAsServer(certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, true);
The certificate is loaded from the X509Store:
X509Certificate2 certificate;
var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, "CN=localhost", false);
store.Close();
if (certificates.Count == 0)
{
Console.WriteLine("Server certificate not found...");
return;
}
else
{
certificate = certificates[0];
}
This also works well if i force the clients to trust the certificate manually.
My questions are:
How can I force the (all) clients to trust the certificate?
Which kind of certificate which is valid for all clients do I need on the proxy?
If needed, what kind of client certificate do I have to install to force the clients to trust the proxy?
How can I create the needed kinds of proxy with openssl or makecert?
I don't want to tunnel the SSL communication threw the proxy because I need to read and manipulate the streams.
[UPDATE]
Yes I used Google and the search in StackOverflow and I tried some different solution without any success.
I also tried the solutions in the following threads:
SSLStream example - how do I get certificates that work?
How do I identify my server name for server authentication by client in c#
[UPDATE2]
This is a very good tutorial to create a CA and a server certificate with openssl, but it doesn't work for me:
http://webserver.codeplex.com/wikipage?title=HTTPS&referringTitle=Home
There is no single certificate which is valid for all requests. So my idea doesn't work.
Because it is not possible to generate a single license for every domain name.
But the Answer is easier than expected: To solve my problem I have to create a certificate for every single request.
All I need to do is:
to create a self-signed root certificate as a certificate authority
to install this in the clients "Trusted Root Certification Authorities" store.
to create the server certificates on-the-fly for every incoming request
sign this certificates with the root certificates tree to set the issuer
I can cache the certificates in files or in a system certificate store if needed.
(this is completely the same in fiddler)
Using .NET 4.0 framework I'm trying to make a server to server HTTPS SOAP service call.
On the client side server, I'm trying to add a local SSL certificate so that the server receiving the call could identify me.
The certificate I'm trying to add is not from the local machine's store (the problem persists even if it is), just a (.cer) file placed as a local file on my machine.
The code works fine and seems to add the certificate, but my System.Net.trace.log file keeps adding the following error for every call:
"Cannot find the certificate in either the LocalMachine store or the CurrentUser store." and the server I'm trying to call keeps telling me there's no certificate attached to my request.
The final WebException I get is:
"The request was aborted: Could not create SSL/TLS secure channel."
My GetCertificate function:
private X509Certificate2 GetCertificate()
{
X509Certificate2 cert;
try
{
cert = new X509Certificate2("MyCertificateFoder\\MyCertificate.cer");
}
catch (Exception ex)
{
throw;
}
return cert;
}
Service call code:
X509Certificate2 cert = GetCertificate();
ServiceClient.ClientCertificates.Add(cert);
serviceMethodResponse = ServiceClient.ServiceMethod(serviceMethodRequest);
Will appreciate any advice.
Thanks
The principal mistake here is that when using SSL with client authentication, a .pfx file (with client private key, certificates, certificate chains, root authority certificates) should be used.
NOT the .cer file.
This is the cause of the error.
I have a fully operational system where openssl based clients interact with an openssl server. Each client have its own certificate that is validated by the server. Certificates have been generated with openssl (X509, pem). They are self-signed.
I now want to write a test client based on SslStream. I used the client example from the SslStream class documentation.
My SslStream client is unable to complete the handshake. stunnel complains about the client not sending its certificate. This is confirmed in Wireshark (Certificates Length: 0 in handshake messages).
My client displays the following exception:
Inner exception: The message received was unexpected or badly
formatted
This is how I load my certificate:
X509Certificate cert = new X509Certificate2(filename, password);
X509CertificateCollection certColl = new X509CertificateCollection();
certColl.Add(cert);
I tried retrieving certificate various properties (ex: GetSerialNumberString()). It works. The Verify method returns false. This is the next thing I am going to investigate.
How I setup my SslStream does not seem to matter (same result):
sslStream.AuthenticateAsClient(serverName);
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectLocalCertificate));
Same with authentication:
sslStream.AuthenticateAsClient(serverName);
sslStream.AuthenticateAsClient(serverName,
certColl,
SslProtocols.Tls,
true);
SelectLocalCertificate gets called (twice) and returns my certificate. ValidateServerCertificate currently never gets called (to my surprise).
How can I debug that? Even better if you can nail my problem.
UPDATE
I have added a function to perform chain validation based on the X509Chain example from the documentation. It displays all sorts of information on the certificate, including two intriguing messages:
Element certificate is valid: False
Element error status length: 1
In the end, I don't really have more details than when I call verify.
The output of openssl verify cert.pem does not report anything unusual.
error 18 at 0 depth lookup:self signed certificate
OK
UPDATE
I extracted the private key and certificate from the pem, and I generated a cert.pfx (pkcs12). I had no issues importing cert.pfx in my personal key store. In my certificate details, I can see a small icon indicating an attached private key.
I modified my client to retrieve the certificate from my personal store. But I am getting the same exact failure.
The solution was to import my CA root certificate on my Windows machine. My client is now able to complete the handshake!
Found the solution by searching for a more complete SslStream example. Thanks to http://geekswithblogs.net/luskan/archive/2007/10/01/115758.aspx.
I have a weird requirement. I am trying to communicate with a server written in C#. It looks like this basically:
SslStream sslStream = new SslStream(client.GetStream(), true,
ValidateServerCertificate,
SelectLocalCertificate);
sslStream.AuthenticateAsServer(_pushCert);
I also have example code in C# that uses a X509 certificate and connects to the server. I have the password for the cert.pfx file as well.
What I would like to do is setup some kind of shell script that can connect to the socket, transmit a few bytes and receive the response. (any language really, although I was looking at Python or Ruby or Perl)
I tried using the SSL wrapper from Python, but I get an error stating their is no known algorithm for the server/client to talk.
Example of my Python code:
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = ssl.wrap_socket(ss, ca_certs=CERT, ssl_version=ssl.PROTOCOL_SSLv23 )
#Attempt connection to our server
try:
s.connect((HOST, PORT))
print s
except:
print 'ERROR Connecting'
sys.exit(0)
For CERT I tried a few different filee: the .pfx, and some extracted from the .pfx using openssl.
I tried many different examples as well (Arguments for the ssl.wrap_socket). I am not really familiar with these connections either.
Perhaps someone here could lend a hand?
Thanks!
You can simplify your SslStream constructor call:
SslStream sslStream = new SslStream(client.GetStream());
sslStream.AuthenticateAsServer(_pushCert);
This server sends _pushCert and does not expect the client to send a certificate back. The server needs the private key for the certificate to make the SSL connection.
The client only needs the CA root certificate that signed the server certificate (or, an option to accept an untrusted certificate.) This needs to be in the "trusted root certificate store" or otherwise identified as trusted to the client wrapper.
If the server certificate is signed by an intermediate CA certificate that is itself signed by the root CA certificate, the client needs that intermediate certificate too. That can be sent by the server, or can already be at the client. Either way, the entire chain of signing certificates has to be in hand at the client to verify all of the signatures along the chain. The intermediate CA certificate does not need to be in the trusted root store.
Neither side needs a private key for the CA root, or for an intermediate signing certificate.
However, if your server expects the client to send a client certificate, then you have to call AuthenticateAsServer with more arguments (clientCertificateRequired == true). In that case, the client needs both its own certificate and the private key for its certificate. The server needs the CA root that signs the client certificate in its trusted store. The client wrapper will take a pfx file, for example, containing the client certificate and private key. The server does not need the client's private key.