Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SslStream Authentication fails under LOCAL SYSTEM account

I have this code:

string certificateFilePath = @"C:\Users\Administrator\Documents\Certificate.pfx";

string certificateFilePassword = "Some Password Here";

X509Certificate clientCertificate = new X509Certificate(certificateFilePath, certificateFilePassword);

TcpClient client = new TcpClient(host, port);

SslStream stream = new SslStream(client.GetStream(), false, (sender, certificate, chain, errors) => true);

X509CertificateCollection clientCertificates = new X509CertificateCollection {clientCertificate};

stream.AuthenticateAsClient(host, clientCertificates, SslProtocols.Tls, false);

When I run the code in a Console Application, everything works fine, stream.IsAuthenticated and stream.IsMutuallyAuthenticated return true and stream.LocalCertificate contains the correct certificate object.

However when running the exact same code in a Windows Service (as LOCAL SYSTEM user), although stream.IsAuthenticated returns true, stream.IsMutuallyAuthenticated returns false and stream.LocalCertificate returns null.

This happens while in both scenarios, after the first line is ran clientCertificate loads the correct certification data and contains the correct information for Certificate's Subject and Issuer.

I have also tried forcing the SslStream to pick the Certificate using this code:

string certificateFilePath = @"C:\Users\Administrator\Documents\Certificate.pfx";

string certificateFilePassword = "Some Password Here";

X509Certificate clientCertificate = new X509Certificate(certificateFilePath, certificateFilePassword);

TcpClient client = new TcpClient(host, port);

SslStream stream = new SslStream(client.GetStream(), false, (sender, certificate, chain, errors) => true, (sender, host, certificates, certificate, issuers) => clientCertificate);

X509CertificateCollection clientCertificates = new X509CertificateCollection {clientCertificate};

stream.AuthenticateAsClient(host, clientCertificates, SslProtocols.Tls, false);

However the code still doesn't work and stream.IsMutuallyAuthenticated returns false and stream.LocalCertificate returns null.

I have been exploring this for a few days now and I can't figure it out. Any help is highly appreciated.

Edit: After trying the certificate with WinHttpCertCfg tool it turns out that unlike similar question(s), LOCAL SYSTEM account already has access to the private key for the target certificate as you can see in the picture below: Output from WinHttpCertCfg tool for the target certificate Therefore the problem still remains unsolved.

like image 238
Rojan Gh. Avatar asked Oct 18 '22 09:10

Rojan Gh.


1 Answers

I finally made the code work while playing around with X509 classes.

Here is the code which works for me:

string host = "The Host";

int port = 777;

string certificateFilePath = @"C:\Users\Administrator\Documents\Certificate.pfx";

string certificateFilePassword = "Some Password Here";

X509Certificate clientCertificate = new X509Certificate(certificateFilePath, certificateFilePassword);

X509Certificate2 clientCertificate2 = new X509Certificate2(clientCertificate); //<== Create a X509Certificate2 object from the X509Certificate which was loaded from the file. The clientCertificate2 loads the proper data

TcpClient client = new TcpClient(host, port);

SslStream stream = new SslStream(client.GetStream(), false, (sender, certificate, chain, errors) => true);

X509CertificateCollection clientCertificates = new X509CertificateCollection { clientCertificate2 }; //<== Using the clientCertificate2 which has loaded the proper data instead of the clientCertificate object

stream.AuthenticateAsClient(host, clientCertificates, SslProtocols.Tls, false);

This way my code finds the proper X509Store, certificate and Private Key from the system.

I have figured this out by experience. I couldn't find a clear explanations on why it should be like this on MSDN though.

like image 199
Rojan Gh. Avatar answered Nov 03 '22 23:11

Rojan Gh.