Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't send client certificate via SslStream

I am doing an SSL3 handshake using an SslStream, but, in spite of my best efforts, the SslStream never sends a client certificate on my behalf. Here is the code:

SSLConnection = new System.Net.Security.SslStream(SSLInOutStream, false, new System.Net.Security.RemoteCertificateValidationCallback(AlwaysValidRemoteCertificate), new System.Net.Security.LocalCertificateSelectionCallback(ChooseLocalCertificate));

X509CertificateCollection CC = new X509CertificateCollection();
CC.Add(Org.BouncyCastle.Security.DotNetUtilities.ToX509Certificate(MyLocalCertificate));

SSLConnection.AuthenticateAsClient("test", CC, System.Security.Authentication.SslProtocols.Ssl3, false);

and then I have AlwaysValidRemoteCertificate just returning true, and ChooseLocalCertificate returning the zeroth element of the array.

The code probably looks a little weird because the project is a little weird, but I think that is beside the point here. The SSL handshake completes. The issue is that instead of sending a certificate message on my behalf (in the handshake process), with the ASN.1 encoded certificate (MyLocalCertificate), the SslStream sends an SSL alert number 41 (no certificate) and then carries on. I know this from packet sniffing. After the handshake is completed, the SslStream marks IsAuthenticated as true, IsMutuallyAuthenticated as false, and its LocalCertificate member is null.

I feel like I'm probably missing something pretty obvious here, so any ideas would be appreciated. I am a novice with SSL, and this project is off the beaten path, so I am kind of at a loss.

P.S. 1: My ChooseLocalCertificate routine is called twice during the handshake, and returns a valid (as far as I can tell), non-null certificate both times.

P.S. 2: SSLInOutStream is my own class, not a NetworkStream. Like I said, though, the handshake proceeds mostly normally, so I doubt this is the culprit... but who knows?

like image 609
Jonathan Avatar asked Dec 17 '22 00:12

Jonathan


2 Answers

For some reason the site won't let me leave a comment on the solution posted above, but it solved my problem. Here's my comment:

Hey, that was a great guess, and it fixed my issue. Thank you very much. The C# X509Certificate class doesn't seem to support setting a private key, but the daughter class X509Certificate2 does. I had to monkey around a bit with BouncyCastle stuff, but the implementation (if anyone needs it in the future) is this:

X509CertificateCollection CC = new X509CertificateCollection();
X509Certificate2 C2 = new X509Certificate2(MyLocalCertificate.GetEncoded());
C2.PrivateKey = Org.BouncyCastle.Security.DotNetUtilities.ToRSA((Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters)MyPrivateKey.Private);
CC.Add(C2);

MyLocalCertificate is an instance of bouncycastle's X509Certificate class, and MyPrivateKey is an instance of bouncycastle's AsymmetricCipherKeyPair.

like image 148
Jonathan Avatar answered Dec 24 '22 01:12

Jonathan


I'm not familiar with the Bouncycastle .NET API for SSL, but at first look, I'm guessing that you aren't supplying the private key to the API.

Even though the private key itself is never sent to the server, it is required to digitally sign some data in order to prove to the server that you hold it. There should be some API to provide the private key for this signature operation.

like image 24
erickson Avatar answered Dec 24 '22 01:12

erickson