web-dev-qa-db-ja.com

.NetSslStreamとクライアント証明書

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

何を試しても、サーバーは常に「クライアント証明書がありません」と表示します。

13
Elkvis

結局のところ、ここではAuthenticateAsServerが重要です。具体的には、2番目のパラメーターです。

clientCertificateRequiredfalseの場合、クライアントによって指定されていても、クライアント証明書は完全に無視されますが、trueの場合は許可されますが、許可されません。クライアント証明書が指定されていない場合は、例外をスローします。

愚かな私-clientCertificateRequiredをtrueに設定すると、.Netドキュメントで次のように説明されているため、実際に必要になると思いました。

"クライアントが認証用の証明書を提供するかどうかを指定するブール値必須。" *(私の強調)

私の予想は、それがtrueであり、クライアント証明書を送信しなかった場合、失敗することでした。これは、Microsoft側の完全に正確ではないドキュメントの明らかなケースです。

更新: 最新のドキュメントclientCertificateRequiredパラメータにはフレーズが含まれています "これは単なるリクエストであることに注意してください-証明書が提供されていない場合でも、サーバーは接続要求を受け入れます。」

9
Elkvis