在TLS Web套接字服务器中使用SslStream的问题 [英] Problems using SslStream in a TLS web socket server

查看:884
本文介绍了在TLS Web套接字服务器中使用SslStream的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遵循本示例来创建我的测试证书。我对客户端使用 Certificate.cer 作为服务器和 Certificate.pfx

I followed this example to create my test certificates. I used Certificate.cer for the server and Certificate.pfx for the client:

makecert -r -pe -nCN =测试证书-sky exchange Certificate.cer -sv Key.pvk -eku 1.3.6.1.5.5.7.3.1,1.3 .6.1.5.5.7.3.2

C:\Program Files(x86)\Windows Kits \8.1\bin\x64\pvk2pfx.exe-pvk Key.pvk -spc Certificate.cer -pfx Certificate.pfx

我试图创建一个Web套接字服务器,并正确地验证来自客户端和服务器端的通信的证书。这是我目前正在构建的整个控制台应用程序:

I am trying to create a web socket server and properly validate certificates from both the client and server sides of the communication. Here is my entire console application which I am currently building:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace WebSockets
{    
    class Program
    {
        static void Main(string[] args)
        {
            CreateWebSocketClient(CreateWebSocketServer(1337), 1338);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        private static IPEndPoint CreateWebSocketServer(int port)
        {
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            IPEndPoint endpoint = new IPEndPoint(IPAddress.Loopback, port);
            socket.Bind(endpoint);
            socket.Listen(Int32.MaxValue);
            socket.BeginAccept((result) =>
            {
                var clientSocket = socket.EndAccept(result);
                Console.WriteLine("{0}: Connected to the client at {1}.", DateTime.Now, clientSocket.RemoteEndPoint);
                using (var stream = new SslStream(new NetworkStream(clientSocket), false, (sender, certificate, chain, sslPolicyErrors) =>
                    {
                        return true;
                    }, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) =>
                    {
                        return new X509Certificate2("Certificate.pfx");
                    }, EncryptionPolicy.RequireEncryption))
                {
                    stream.AuthenticateAsServer(new X509Certificate2("Certificate.pfx"), true, SslProtocols.Tls12, true);
                    stream.Write("Hello".ToByteArray());
                    Console.WriteLine("{0}: Read \"{1}\" from the client at {2}.", DateTime.Now, stream.ReadMessage(), clientSocket.RemoteEndPoint);
                }
            }, null);
            Console.WriteLine("{0}: Web socket server started at {1}.", DateTime.Now, socket.LocalEndPoint);
            return endpoint;
        }

        private static void CreateWebSocketClient(IPEndPoint remoteEndpoint, int port)
        {
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Loopback, port);
            socket.Bind(localEndpoint);
            socket.BeginConnect(remoteEndpoint, (result) =>
            {
                socket.EndConnect(result);
                Console.WriteLine("{0}: Connected to the server at {1}.", DateTime.Now, remoteEndpoint);
                using (var stream = new SslStream(new NetworkStream(socket), false, (sender, certificate, chain, sslPolicyErrors) =>
                    {
                        return true;
                    }, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) =>
                    {
                        return new X509Certificate2("Certificate.cer");
                    }, EncryptionPolicy.RequireEncryption))
                {
                    stream.AuthenticateAsClient(remoteEndpoint.ToString(), new X509Certificate2Collection(new X509Certificate2[] { new X509Certificate2("Certificate.cer") }), SslProtocols.Tls12, true);
                    stream.Write("Hello".ToByteArray());
                    Console.WriteLine("{0}: Read \"{1}\" from the server at {2}.", DateTime.Now, stream.ReadMessage(), remoteEndpoint);
                }
            }, null);
        }
    }

    public static class StringExtensions
    {
        public static Byte[] ToByteArray(this String value)
        {
            Byte[] bytes = new Byte[value.Length * sizeof(Char)];
            Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        public static String FromByteArray(this Byte[] bytes)
        {
            Char[] characters = new Char[bytes.Length / sizeof(Char)];
            Buffer.BlockCopy(bytes, 0, characters, 0, bytes.Length);
            return new String(characters).Trim(new Char[] { (Char)0 });
        }

        public static int BufferSize = 0x400;

        public static String ReadMessage(this SslStream stream)
        {
            var buffer = new Byte[BufferSize];
            stream.Read(buffer, 0, BufferSize);
            return FromByteArray(buffer);
        }
    }
}

服务器和客户端之间的通信当你运行它,但我不知道如何实现回调,特别是因为 sslPolicyErrors = RemoteCertificateNotAvailable RemoteCertificateValidationCallback 在服务器端调用, sslPolicyErrors = RemoteCertificateNameMismatch | RemoteCertificateChainErrors RemoteCertificateValidationCallback 在客户端调用。此外, certificate chain 在服务器端为空,但显示在来自客户端的回调中。这是为什么?我的实现有什么问题,如何使我的实现正确验证SSL证书?我已经尝试在线搜索 SslStream ,但我还没有看到一个完整的,基于X509的TLS服务器客户端实现,我需要的证书验证类型。 p>

Communication between server and client works fine when you run it, but I am not sure how I should implement the callbacks, specifically because sslPolicyErrors = RemoteCertificateNotAvailable when the RemoteCertificateValidationCallback is called on the server side and sslPolicyErrors = RemoteCertificateNameMismatch | RemoteCertificateChainErrors when the RemoteCertificateValidationCallback is called on the client side. Also, certificate and chain are null on the server side but appear on the callback from the client side. Why is that? What are the problems with my implementation and how can I make my implementation validate SSL certificates properly? I have tried searching online about the SslStream but I have yet to see a full, X509-based TLS server-client implementation that does the type of certificate validation I need.

推荐答案

我有三个独立的问题。我的初始方法很好,但是:

I had three separate problems. My initial approach was good, but:


  1. 我在这里误用了证书,因为使用 .pfx 证书解决了我的 RemoteCertificateNotAvailable 问题。我不确定为什么 .cer 无法工作。

  1. I have misused certificates here, as using the .pfx certificate on the client side resolves my RemoteCertificateNotAvailable problem. I am not sure as to why the .cer did not work.

在调用 AuthenticateAsClient 时,使用Test Certificate代替 remoteEndpoint.ToString()解决我的 RemoteCertificateNameMismatch

I have specified the wrong subject name in my call to AuthenticateAsClient, as using "Test Certificate" for the first argument instead of remoteEndpoint.ToString() solves my RemoteCertificateNameMismatch.

尽管自签名, c> RemoteCertificateChainErrors 错误,我必须将此证书添加到我当前用户帐户下的受信任的人员商店才能信任该证书。

Despite being self-signed, to get around the RemoteCertificateChainErrors error, I had to add this certificate to the Trusted People store under my current user account in order to trust the certificate.

包括了一些其他的小改进,我的结果代码,现在接受多个客户端(因为我修复了上面的一些错误),如下( t逐字复制,因为它在不同的地方需要大量的宠物小精灵例外处理清理逻辑,利用Read读取的字节而不是修剪NUL,并引入一些Unicode字符(如EOT)来指定消息的结束,解析它,以及处理奇数大小的缓冲区不支持,因为我们的C#字符大小是2字节,处理奇数读取等;这需要大量的细化,才能看到生产系统的光,并只作为一个例子或概念的证明,如果你会。):

Some other small refinements included, and my resulting code, which accepts multiple clients now as well (as I had fixed some bugs above), is as follows (please don't copy this verbatim as it needs a lot of Pokemon exception handling in different places, proper clean-up logic, making use of the bytes read on Read calls instead of trimming NUL, and the introduction of some Unicode character such as EOT to specify the end of messages, parsing for it, as well as handling of odd sized buffers which are not supported since our C# character size is 2 bytes, handling of odd reads, etc.; this needs a lot of refinement before it ever sees the light of a production system and serves only as an example or a proof of concept, if you will.):

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WebSockets
{
    class Program
    {
        static void Main(string[] args)
        {
            IPEndPoint server = CreateWebSocketServer(1337);
            CreateWebSocketClient(server, 1338);
            CreateWebSocketClient(server, 1339);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        private static IPEndPoint CreateWebSocketServer(int port)
        {
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            IPEndPoint endpoint = new IPEndPoint(IPAddress.Loopback, port);
            socket.Bind(endpoint);
            socket.Listen(Int32.MaxValue);
            ListenForClients(socket);
            Console.WriteLine("{0}: Web socket server started at {1}.", DateTime.Now, socket.LocalEndPoint);
            return endpoint;
        }

        private static void ListenForClients(Socket socket)
        {
            socket.BeginAccept((result) =>
            {
                new Thread(() =>
                {
                    ListenForClients(socket);
                }).Start();
                var clientSocket = socket.EndAccept(result);
                Console.WriteLine("{0}: Connected to the client at {1}.", DateTime.Now, clientSocket.RemoteEndPoint);
                using (var stream = new SslStream(new NetworkStream(clientSocket), false, (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (sslPolicyErrors == SslPolicyErrors.None)
                        return true;
                    return false;
                }, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) =>
                {
                    return new X509Certificate2("Certificate.pfx");
                }, EncryptionPolicy.RequireEncryption))
                {
                    stream.AuthenticateAsServer(new X509Certificate2("Certificate.pfx"), true, SslProtocols.Tls12, true);
                    stream.Write("Hello".ToByteArray());
                    Console.WriteLine("{0}: Read \"{1}\" from the client at {2}.", DateTime.Now, stream.ReadMessage(), clientSocket.RemoteEndPoint);
                }
            }, null);
        }

        private static void CreateWebSocketClient(IPEndPoint remoteEndpoint, int port)
        {
            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Loopback, port);
            socket.Bind(localEndpoint);
            socket.BeginConnect(remoteEndpoint, (result) =>
            {
                socket.EndConnect(result);
                Console.WriteLine("{0}: Client at {1} connected to the server at {2}.", DateTime.Now, localEndpoint, remoteEndpoint);
                using (var stream = new SslStream(new NetworkStream(socket), false, (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (sslPolicyErrors == SslPolicyErrors.None)
                        return true;
                    return false;
                }, (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) =>
                {
                    return new X509Certificate2("Certificate.pfx");
                }, EncryptionPolicy.RequireEncryption))
                {
                    stream.AuthenticateAsClient("Test Certificate", new X509Certificate2Collection(new X509Certificate2[] { new X509Certificate2("Certificate.pfx") }), SslProtocols.Tls12, true);
                    stream.Write("Hello".ToByteArray());
                    Console.WriteLine("{0}: Client at {1} read \"{2}\" from the server at {3}.", DateTime.Now, localEndpoint, stream.ReadMessage(), remoteEndpoint);
                }
            }, null);
        }
    }

    public static class StringExtensions
    {
        public static Byte[] ToByteArray(this String value)
        {
            Byte[] bytes = new Byte[value.Length * sizeof(Char)];
            Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        public static String FromByteArray(this Byte[] bytes)
        {
            Char[] characters = new Char[bytes.Length / sizeof(Char)];
            Buffer.BlockCopy(bytes, 0, characters, 0, bytes.Length);
            return new String(characters).Trim(new Char[] { (Char)0 });
        }

        public static int BufferSize = 0x400;

        public static String ReadMessage(this SslStream stream)
        {
            var buffer = new Byte[BufferSize];
            stream.Read(buffer, 0, BufferSize);
            return FromByteArray(buffer);
        }
    }
}

我希望这有助于他人揭秘Web套接字,SSL流,X509证书等等。快乐编码。 :)我可能最终在我的博客上发布其最终版本。

I hope this helps others demystify web sockets, SSL streams, X509 certificates, and so forth, in C#. Happy coding. :) I may end up posting its final edition on my blog.

这篇关于在TLS Web套接字服务器中使用SslStream的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆