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: Therefore the problem still remains unsolved.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With