locked
.NET Core TLS1.3 Server-Client Example With Client Auth RRS feed

  • Question

  • Hi. 

    I probe create a .NET Core Server Client example. But my code won't work with client auth. Only works with server only authentication.

    The error:

    *** SSL Error: RemoteCertificateNotAvailable
    *** AuthenticationException
    *** The remote certificate is invalid according to the validation procedure.!

    The TLS13 is Enabled in my Explorer and registry. And the Base example certificates are imported. (but read from file. Tested with imported cert with same result)

    What is my fault? Thanks for a help.

    I download the tls1.2 base code from:
    Base code

    My Server Code:

    using System;
    using System.Net;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Security.Authentication;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    
    namespace TCP_TLS13_Server
    {
        class Program
        {
            private static readonly int ServerPort = 8433;
    
            //private static readonly string ServerCertificateName = "MyServer";
            private static readonly string ServerCertificateFile = "server.pfx";
            private static readonly string ServerCertificatePassword = null;
    
            static void Main(string[] args)
            {
                try
                {
                    ////read from the store (must have a key there)
                    //var store = new X509Store(StoreLocation.CurrentUser);
                    //store.Open(OpenFlags.ReadOnly);
                    //var serverCertificateCollection = store.Certificates.Find(X509FindType.FindBySubjectName, ServerCertificateName, false);
                    //var serverCertificate = serverCertificateCollection[0];
    
                    //read from the file
                    var serverCertificate = new X509Certificate2(ServerCertificateFile, ServerCertificatePassword);
    
                    var listener = new TcpListener(IPAddress.Any, ServerPort);
                    listener.Start();
                    Console.WriteLine("Started listening.");
    
                    while (true)
                    {
                        try
                        {
                            Console.WriteLine();
                            Console.WriteLine("Waiting for a client to connect...");
                            using (var client = listener.AcceptTcpClient())
                            using (var sslStream = new SslStream(client.GetStream(), false, App_CertificateValidation, null, EncryptionPolicy.RequireEncryption)) {
                            //using (var sslStream = new SslStream(client.GetStream())) {
    
                                Console.WriteLine("Accepted client " + client.Client.RemoteEndPoint.ToString());
    
                                sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls13, false);
                                Console.WriteLine("SSL authentication completed.");
                                Console.WriteLine("SSL using local certificate {0}.", sslStream.LocalCertificate.Subject);
                                if (sslStream.RemoteCertificate != null)
                                    Console.WriteLine("SSL using remote certificate {0}.", sslStream.RemoteCertificate.Subject);
    
                                // Display the properties and settings for the authenticated stream.
                                DisplaySecurityLevel(sslStream);
                                DisplaySecurityServices(sslStream);
                                DisplayCertificateInformation(sslStream);
                                DisplayStreamProperties(sslStream);
    
    
                                var outputMessage = "Hello from the server.";
                                var outputBuffer = Encoding.UTF8.GetBytes(outputMessage);
                                sslStream.Write(outputBuffer);
                                Console.WriteLine("Sent: {0}", outputMessage);
    
                                var inputBuffer = new byte[4096];
                                var inputBytes = 0;
                                while (inputBytes == 0)
                                {
                                    inputBytes = sslStream.Read(inputBuffer, 0, inputBuffer.Length);
                                }
                                var inputMessage = Encoding.UTF8.GetString(inputBuffer, 0, inputBytes);
                                Console.WriteLine("Received: {0}", inputMessage);
                            }
                        }
                        catch (Exception ex)
                        {
    
                            Console.WriteLine("*** {0}\n*** {1}!", ex.GetType().Name, ex.Message);
                        }
    
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("*** {0}\n*** {1}!", ex.GetType().Name, ex.Message);
                }
    
                Console.WriteLine();
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey();
            }
    
            private static bool App_CertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            {
                if (sslPolicyErrors == SslPolicyErrors.None) { return true; }
                if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors) { return true; } //we don't have a proper certificate tree
                Console.WriteLine("*** SSL Error: " + sslPolicyErrors.ToString());
                return false;
            }
            static void DisplaySecurityLevel(SslStream stream)
            {
                Console.WriteLine("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
                Console.WriteLine("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
                Console.WriteLine("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
                Console.WriteLine("Protocol: {0}", stream.SslProtocol);
            }
            static void DisplaySecurityServices(SslStream stream)
            {
                Console.WriteLine("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
                Console.WriteLine("IsSigned: {0}", stream.IsSigned);
                Console.WriteLine("Is Encrypted: {0}", stream.IsEncrypted);
            }
            static void DisplayStreamProperties(SslStream stream)
            {
                Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
                Console.WriteLine("Can timeout: {0}", stream.CanTimeout);
            }
            static void DisplayCertificateInformation(SslStream stream)
            {
                Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);
    
                X509Certificate localCertificate = stream.LocalCertificate;
                if (stream.LocalCertificate != null)
                {
                    Console.WriteLine("Local cert was issued to {0} and is valid from {1} until {2}.",
                        localCertificate.Subject,
                        localCertificate.GetEffectiveDateString(),
                        localCertificate.GetExpirationDateString());
                }
                else
                {
                    Console.WriteLine("Local certificate is null.");
                }
                // Display the properties of the client's certificate.
                X509Certificate remoteCertificate = stream.RemoteCertificate;
                if (stream.RemoteCertificate != null)
                {
                    Console.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.",
                        remoteCertificate.Subject,
                        remoteCertificate.GetEffectiveDateString(),
                        remoteCertificate.GetExpirationDateString());
                }
                else
                {
                    Console.WriteLine("Remote certificate is null.");
                }
            }
        }
    }

    My Client Code

    using System;
    using System.Diagnostics;
    using System.Net;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Security.Authentication;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    
    namespace TCP_TLS13_Client
    {
        class Program
        {
            private static readonly string ServerHostName = "localhost";
            private static readonly int ServerPort = 8433;
    
            //private static readonly string ServerHostName = "google.com";
            //private static readonly int ServerPort = 433;
    
    
            private static readonly string ServerCertificateName = "MyServer";
    
            private static readonly string ClientCertificateName = "MyClient";
            private static readonly string ClientCertificateFile = "client.pfx";
            private static readonly string ClientCertificatePassword = null;
    
            static void Main(string[] args)
            {
                try
                {
                    ////read from the store (must have a key there)
                    //var store = new X509Store(StoreLocation.CurrentUser);
                    //store.Open(OpenFlags.ReadOnly);
                    //var clientCertificateCollection = store.Certificates.Find(X509FindType.FindBySubjectName, ClientCertificateName, false);
    
                    //read from the file
                    var clientCertificate = new X509Certificate2(ClientCertificateFile, ClientCertificatePassword);
                    var clientCertificateCollection = new X509CertificateCollection(new X509Certificate[] { clientCertificate });
    
                    using (var client = new TcpClient(ServerHostName, ServerPort))
                    using (var sslStream = new SslStream(client.GetStream(), false, App_CertificateValidation, LocalCertificateSelectionCallback, EncryptionPolicy.RequireEncryption))
                    {
                        // Ensure the client does not close when there is still data to be sent to the server.
                        client.LingerState = (new LingerOption(true, 0));
    
                        Console.WriteLine("Client connected.");
    
                        sslStream.AuthenticateAsClient(ServerCertificateName, clientCertificateCollection, SslProtocols.Tls13, false); //For Server and Client auth
                        //IPAddress IP = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
                        //sslStream.AuthenticateAsClient(ServerCertificateName, null, SslProtocols.Tls13, false); //For Server  auth
                        
                        Console.WriteLine("SSL authentication completed.");
                        if (sslStream.LocalCertificate != null)
                            Console.WriteLine("SSL using local certificate {0}.", sslStream.LocalCertificate.Subject);
                        Console.WriteLine("SSL using remote certificate {0}.", sslStream.RemoteCertificate.Subject);
                        
                        var outputMessage = "Hello from the client " + Process.GetCurrentProcess().Id.ToString() + ".";
                        var outputBuffer = Encoding.UTF8.GetBytes(outputMessage);
                        sslStream.Write(outputBuffer);
                        Console.WriteLine("Sent: {0}", outputMessage);
    
                        var inputBuffer = new byte[4096];
                        var inputBytes = 0;
                        while (inputBytes == 0)
                        {
                            inputBytes = sslStream.Read(inputBuffer, 0, inputBuffer.Length);
                        }
                        var inputMessage = Encoding.UTF8.GetString(inputBuffer, 0, inputBytes);
                        Console.WriteLine("Received: {0}", inputMessage);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("*** {0}\n*** {1}!", ex.GetType().Name, ex.Message);
                }
    
                Console.WriteLine();
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey();
            }
    
            public static X509Certificate LocalCertificateSelectionCallback(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
            {
                return localCertificates[0];
            }
    
            private static bool App_CertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            {
                if (sslPolicyErrors == SslPolicyErrors.None) { return true; }
                if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors) { return true; } //we don't have a proper certificate tree
                Console.WriteLine("*** SSL Error: " + sslPolicyErrors.ToString());
                return false;
            }
        }
    }


    Friday, February 14, 2020 6:31 PM

All replies

  • Hi graisoft1982,

    Thank you for posting here.

    It seems that your certificate validation method return 'false'.

    You can use the following code to ask the user when the certificate is not valid whether he wants to continue or not. 

            private static bool App_CertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            {
                if (sslPolicyErrors == SslPolicyErrors.None)
                    return true;
                else
                {
                    if (System.Windows.Forms.MessageBox.Show("The server certificate is not valid.\nAccept?", "Certificate Validation", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
                        return true;
                    else
                        return false;
                }
            }

    Besides, here's a reference about the exception.

    The remote certificate is invalid according to the validation procedure

    Hope it can help you.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, February 17, 2020 7:43 AM
  • Hi Xingyu Zhao.

    Thanks for a reply. The retun true in the App_CertificateValidation is helped for a simple, only server side authentication. 

    But the sslStream.RemoteCertificate == null;

    Unfortunently on the client side the parameters of App_CertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) is

    sender = null, certificate = null, chain = null.

    So I cant authenticate my client by certificate fingerprint.

    Do you have any idea why there is no client side certification on the server side? On the client side the Server certificate work well.

    Thanks for your reply.

    Wednesday, February 19, 2020 9:07 PM