使用客户端证书的 .Net SslStream

14

我在使用SslStream项目时无法让客户端证书正常工作。无论我做了什么,它都不能真正使用客户端证书,尽管所有证书都是有效的和可信任的,我已经导入了我自己生成的那些证书的CA证书,但它就是不起作用。我必定漏掉了什么,但我已经反复检查了数十次,查阅了文档、例子和数小时的谷歌搜索,但我就是无法让它工作。我做错了什么?

客户端:

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;
        }
    }
}

服务器:

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;
        }
    }
}
无论我尝试什么,服务器总是显示“无客户端证书”。
2个回答

16
事实证明,AuthenticateAsServer 是关键 - 更具体地说是第二个参数。
如果 clientCertificateRequiredfalse,它将完全忽略客户端证书,即使客户端指定了一个,但如果它为 true,它将允许客户端证书,但如果没有指定客户端证书,则不会抛出异常。
我太傻了 - 我认为将 clientCertificateRequired 设置为 true 意味着它会实际上被要求,因为 .Net 文档将其描述为:
"A Boolean value that specifies whether the client must supply a certificate for authentication."* (强调我的)
我的期望是,如果它为 true,并且我没有发送客户端证书,那么它将失败。这是微软文档不够准确的一个明显例子。
更新:对于 clientCertificateRequired 参数的最新文档包括短语 "Note that this is only a request -- if no certificate is provided, the server still accepts the connection request."

0

我有同样的问题,我的解决方案是:我将clientCertificateRequired设置为true,并使用以下代码手动检查clientCertificate。

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;
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接