简体   繁体   中英

.Net SslStream with Client Certificate

I'm having no luck getting client certificates working with my SslStream project. No matter what I do, I can't get it to actually use the client certificate, despite the fact that all certificates are valid and trusted, and I have imported the CA certificate for the ones I generated myself, and it just doesn't work. I must be missing something, but I've been over it dozens of times, reviewed documentation, examples, and hours of google searching, and I just can't get it to work. What am I doing wrong?

The Client:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace SslClient
{
    class SslClientProgram
    {
        static void Main(string[] args)
        {
            TcpClient client = new TcpClient("localhost", 443);

            SslStream stream = new SslStream(client.GetStream(), false, VerifyServerCertificate, null);

            Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
            string location = assembly.Location;
            int pos = location.LastIndexOf('\\');
            location = location.Substring(0, pos);
            X509Certificate2 certificate = new X509Certificate2(location + "\\my.client.certificate.pfx", "password");

            stream.AuthenticateAsClient("my.host.name", new X509Certificate2Collection(certificate), System.Security.Authentication.SslProtocols.Tls, false);

            StreamReader reader = new StreamReader(stream);
            StreamWriter writer = new StreamWriter(stream);

            while (true)
            {
                string line = System.Console.ReadLine();
                writer.WriteLine(line);
                writer.Flush();
                if (line == "close") break;
                line = reader.ReadLine();
                System.Console.WriteLine("Received: {0}", line);
            }

            stream.Close();
        }

        private static bool VerifyServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
    }
}

The Server:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace SslServer
{
    class SslServerProgram
    {
        static void Main(string[] args)
        {
            TcpListener server = new TcpListener(System.Net.IPAddress.Loopback, 443);

            server.Start();

            TcpClient client = server.AcceptTcpClient();

            SslStream stream = new SslStream(client.GetStream(), false, VerifyClientCertificate, null);

            Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
            string location = assembly.Location;
            int pos = location.LastIndexOf('\\');
            location = location.Substring(0, pos);
            X509Certificate2 certificate = new X509Certificate2(location + "\\my.server.certificate.pfx", "password");

            stream.AuthenticateAsServer(certificate, false, System.Security.Authentication.SslProtocols.Tls, false);

            if (stream.RemoteCertificate != null)
            {
                System.Console.WriteLine(stream.RemoteCertificate.Subject);
            }
            else
            {
                System.Console.WriteLine("No client certificate.");
            }

            StreamReader reader = new StreamReader(stream);
            StreamWriter writer = new StreamWriter(stream);

            bool clientClose = false;
            while (!System.Console.KeyAvailable)
            {
                System.Console.WriteLine("Waiting for data...");
                string line = reader.ReadLine();
                System.Console.WriteLine("Received: {0}", line);

                if (line == "close")
                {
                    clientClose = true;
                    break;
                }

                writer.WriteLine(line);
                writer.Flush();
            }

            if (!clientClose) System.Console.ReadKey();

            stream.Close();
            server.Stop();
        }

        private static bool VerifyClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }
    }
}

No matter what I try, the server always says "No client certificate."

As it turns out, AuthenticateAsServer is the key here - more specifically, the second parameter.

If clientCertificateRequired is false , it will completely ignore client certificates, even if one is specified by the client, but if it is true , it will allow them, but does not throw an exception if no client certificate is specified.

Silly me - I thought clientCertificateRequired set to true meant that it would be actually required , because the .Net documentation describes it as:

"A Boolean value that specifies whether the client must supply a certificate for authentication."* (emphasis mine)

My expectation was that if it was true , and I did not send a client certificate, then it would fail. This is a clear case of less than completely accurate documentation on the part of Microsoft.

Update: The latest documentation for the clientCertificateRequired parameter includes the phrase "Note that this is only a request -- if no certificate is provided, the server still accepts the connection request."

I have same problem and my solution here: I'm set clientCertificateRequired true , and manual checking clientCertificate with code below

 state.SslStream = new SslStream(state.NetworkStream, true , new RemoteCertificateValidationCallback(ValidateServerCertificate), null);

 private bool ValidateServerCertificate(object sender, X509Certificate certificate , X509Chain chain, SslPolicyErrors sslPolicyErrors) { if (certificate == null) return false; var clientCertificate = certificate as X509Certificate2; if (!serverCertificate.Thumbprint.Equals(clientCertificate.Thumbprint)) return false; ///For self-signed certificate, return true. return true; //if (sslPolicyErrors == SslPolicyErrors.None) // return true; //Log(string.Format("Certificate validation error:{0}", sslPolicyErrors)); //return false; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM