如何在C#中找出我的服务器名称服务器认证通过客户端 [英] How do I identify my server name for server authentication by client in c#

查看:190
本文介绍了如何在C#中找出我的服务器名称服务器认证通过客户端的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直试图在C#中的SSL进行加密服务器/客户端。



我遵循的在MSDN上这个教程,但是,它需要一个证书来使用服务器和客户端使用创建的 makecert.exe 的,所以我找到了一个例子,它创造优良证书:




makecert -sr LOCALMACHINE -ss我-nCN =测试-sky交换-SK 123456 C:/Test.cer




但现在的问题是客户端的服务器启动和等待,当客户端连接,它使用的计算机名的其中据我所知是我在这种情况下,IP:




127.0.0.1




,然后它需要的服务器名称的必须匹配的证书上的服务器名称的( Test.cer 的)。我曾尝试多种组合(如测试LOCALMACHINE,127.0.0.1,但不能似乎得到给定的服务器名称的匹配从而使连接我得到的错误是客户端:




证书错误:RemoteCertificateNameMismatch,RemoteCertificateChainErrors
例外:远程证书按验证程序是无效的。




下面是我使用的代码,它从MSDN的例子仅在我指定的证书路径在应用程序和设备服务器的事实不同,



SslTcpServer.cs



<$ p $:客户太的名和服务器名p> 使用系统;
System.Collections中使用;使用的System.Net.Sockets
;使用System.Net.Security

使用System.Net ;
使用System.Security.Cryptography.X509Certificates;
:使用System.IO;
$ b $使用System.Text
;采用System.Security.Authentication
b命名空间Examples.System.Net
{
公共密封类SslTcpServer
{
静态x509证书serverCertificate = NULL;
//证书参数指定文件
的名称//包含计算机证书。
公共静态无效RUNSERVER(串证)
{
serverCertificate = X509Certificate.CreateFromCertFile(证书);
//创建一个TCP / IP(IPv4)的插座,侦听传入的连接。
的TcpListener监听器=新的TcpListener(IPAddress.Any,8080);
listener.Start();
,而(真)
{
Console.WriteLine(等待客户端连接...);
//应用程序块,同时等待传入的连接。
//类型CNTL-C终止服务器。
TcpClient的客户端= listener.AcceptTcpClient();
ProcessClient(客户端);
}
}
静态无效ProcessClient(TcpClient的客户端)
{
//一个客户端已连接。创建使用客户端的网络流
// SslStream。
SslStream sslStream =新SslStream(
client.GetStream(),FALSE);
//验证服务器,但不要求客户端进行身份验证。

{
sslStream.AuthenticateAsServer(serverCertificate,
假,SslProtocols.Tls,真正的);
//显示的验证流属性和设置。
DisplaySecurityLevel(sslStream);
DisplaySecurityServices(sslStream);
DisplayCertificateInformation(sslStream);
DisplayStreamProperties(sslStream);

//超时设置为读写为5秒。
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
//阅读来自客户端的消息。
Console.WriteLine(等待客户端消息......);
串messageData = ReadMessage(sslStream);
Console.WriteLine(收到:{0},messageData);

//写消息给客户端。
的byte []消息= Encoding.UTF8.GetBytes(你好从服务器16。EOF>);
Console.WriteLine(发送hello消息。);
sslStream.Write(消息);
}
赶上(的AuthenticationException E)
{
Console.WriteLine(异常:{0},e.Message);
如果(e.InnerException!= NULL)
{
Console.WriteLine(内部异常:{0},e.InnerException.Message);
}
Console.WriteLine(验证失败 - 关闭连接。);
sslStream.Close();
client.Close();
的回报;
}
终于
{
//客户流将与sslStream
//因为我们创造
时//指定的这一行为被关闭sslStream。
sslStream.Close();
client.Close();
}
}
静态字符串ReadMessage(SslStream sslStream)
{
//读取客户端发送的消息。
//客户信号使用
//消息的结尾<&EOF GT;标记。
字节[]缓冲区=新的字节[2048];
StringBuilder的messageData =新的StringBuilder();
INT字节= -1;

{
//读取客户端的测试消息。
字节= sslStream.Read(缓冲,0,buffer.Length);

//使用解码器类可从字节转换为UTF8
//的情况下,一个字符跨越两个缓冲区。
解码器解码器= Encoding.UTF8.GetDecoder();
的char [] =字符新的char [decoder.GetCharCount(缓冲,0字节)];
decoder.GetChars(缓冲液,0,字节,字符,0);
messageData.Append(字符);
//检查EOF或空消息。
如果(messageData.ToString()的IndexOf(<&EOF GT;)!= -1)
{
中断;
}
},而(字节!= 0);

返回messageData.ToString();
}
静态无效DisplaySecurityLevel(SslStream流)
{
Console.WriteLine(密码:{0}实力{1},stream.CipherAlgorithm,stream.CipherStrength);
Console.WriteLine(哈希:{0}实力{1},stream.HashAlgorithm,stream.HashStrength);
Console.WriteLine(密钥交换:{0}实力{1},stream.KeyExchangeAlgorithm,stream.KeyExchangeStrength);
Console.WriteLine(协议:{0},stream.SslProtocol);
}
静态无效DisplaySecurityServices(SslStream流)
{
Console.WriteLine(通过身份验证:{0}为服务器{1}?,stream.IsAuthenticated,流。 IsServer);
Console.WriteLine(IsSigned:{0},stream.IsSigned);
Console.WriteLine(已加密:{0},stream.IsEncrypted);
}
静态无效DisplayStreamProperties(SslStream流)
{
Console.WriteLine(能读:{0},写{1},stream.CanRead,stream.CanWrite );
Console.WriteLine(可以超时:{0},stream.CanTimeout);
}
静态无效DisplayCertificateInformation(SslStream流)
{
Console.WriteLine(证书吊销列表检查:{0},stream.CheckCertRevocationStatus);

x509证书localCertificate = stream.LocalCertificate;
如果(stream.LocalCertificate!= NULL)
{
Console.WriteLine(本地证书颁发给{0}和从{1}至{2}。有效,
localCertificate.Subject,
localCertificate.GetEffectiveDateString(),
localCertificate.GetExpirationDateString());
}
,否则
{
Console.WriteLine(本地证书为空);
}
//显示客户端证书的属性。
x509证书remoteCertificate = stream.RemoteCertificate;
如果(stream.RemoteCertificate!= NULL)
{
Console.WriteLine(远程证书颁发给{0}和从{1}至{2}。有效,
remoteCertificate.Subject,
remoteCertificate.GetEffectiveDateString(),
remoteCertificate.GetExpirationDateString());
}
,否则
{
Console.WriteLine(远程认证为空);
}
}
公共静态无效的主要(字串[] args)
{
串证=C:/Test.cer
SslTcpServer.RunServer(证书);
}
}
}



SslTcpClient.cs

 使用系统; System.Collections中使用
;使用System.Net
;
使用System.Net.Security;使用的System.Net.Sockets
;
使用System.Security.Authentication;
使用System.Text;使用System.Security.Cryptography.X509Certificates
;
:使用System.IO;

命名空间Examples.System.Net
{
公共类SslTcpClient
{
私人静态Hashtable的certificateErrors =新的Hashtable();

//以下方法由RemoteCertificateValidationDelegate调用。
公共静态布尔ValidateServerCertificate(
对象发件人,
X509证书证书,
X509Chain链,
SslPolicyErrors sslPolicyErrors)
{
如果(sslPolicyErrors = = SslPolicyErrors.None)
返回真;

Console.WriteLine(证书错误:{0},sslPolicyErrors);

//不要让这个客户与未经认证的服务器通信。
返回FALSE;
}
公共静态无效RunClient(字符串计算机名,字符串服务器名)
{
//创建一个TCP / IP客户端套接字。
//机器是运行服务器应用程序的主机。
TcpClient的客户端=新的TcpClient(机器名,8080);
Console.WriteLine(客户端连接。);
//创建一个SSL数据流,将关闭客户端的数据流。
SslStream sslStream =新SslStream(
client.GetStream(),
假,
新RemoteCertificateValidationCallback(ValidateServerCertificate),

);
//服务器名称必须与服务器证书的名称相匹配。

{
sslStream.AuthenticateAsClient(服务器);
}
赶上(的AuthenticationException E)
{
Console.WriteLine(异常:{0},e.Message);
如果(e.InnerException!= NULL)
{
Console.WriteLine(内部异常:{0},e.InnerException.Message);
}
Console.WriteLine(验证失败 - 关闭连接。);
client.Close();
的回报;
}
//编码测试消息成字节数组。
//信号中的消息的使用结束<&EOF GT;。
字节[] =一封邮件Encoding.UTF8.GetBytes(你好从客户端<&EOF GT;);
//发送hello消息到服务器。
sslStream.Write(一封邮件);
sslStream.Flush();从服务器
//读消息。
串SERVERMESSAGE = ReadMessage(sslStream);
Console.WriteLine(服务器说:{0},SERVERMESSAGE);
//关闭客户端连接。
client.Close();
Console.WriteLine(客户端关闭。);
}
静态字符串ReadMessage(SslStream sslStream)
{
//读取服务器发送的消息。
//消息的末尾使用
//信号<&EOF GT;标记。
字节[]缓冲区=新的字节[2048];
StringBuilder的messageData =新的StringBuilder();
INT字节= -1;

{
字节= sslStream.Read(缓冲,0,buffer.Length);

//使用解码器类可从字节转换为UTF8
//的情况下,一个字符跨越两个缓冲区。
解码器解码器= Encoding.UTF8.GetDecoder();
的char [] =字符新的char [decoder.GetCharCount(缓冲,0字节)];
decoder.GetChars(缓冲液,0,字节,字符,0);
messageData.Append(字符);
//检查EOF。
如果(messageData.ToString()的IndexOf(<&EOF GT;)!= -1)
{
中断;
}
},而(字节!= 0);

返回messageData.ToString();
}
公共静态无效的主要(字串[] args)
{
字符串serverCertificateName = NULL;
字符串MACHINENAME = NULL;
/ *
//用户可以指定机器名和服务器名。
//服务器名称必须与服务器证书的名称相匹配。
MACHINENAME = ARGS [0];
如果(args.Length 2)
{
serverCertificateName = MACHINENAME;
}
,否则
{
serverCertificateName = ARGS [1];
} * /
MACHINENAME =127.0.0.1;
serverCertificateName =大卫-PC; //试图测试,LOCALMACHINE和127.0.0.1
SslTcpClient.RunClient(机器名,serverCertificateName);
Console.ReadKey();
}
}
}



编辑:



服务器接受客户端的连接和一切,但它超时,同时等待客户端发送消息。 (客户端不会与服务器进行身份验证因证书中的服务器名称是从我在客户端提供的不同)以及多数民众赞成在这我的想法只是为了澄清



更新:



据一个答案我已经改变了certficiate制造商为:




makecert -sr LOCALMACHINE -ss我-nCN = localhost的-sky交换-sk 123456 C:/Test.cer
,并在我的客户我有:




  MACHINENAME =127.0.0.1; 
serverCertificateName =本地主机; //试图测试,LOCALMACHINE和127.0.0.1
SslTcpClient.RunClient(机器名,serverCertificateName);

现在我得到异常:




RemoteCertificateChainErrors
例外:远程证书按验证程序


无效

这是发生这里:

  //服务器名称必须与服务器证书的名称相匹配。 

{
sslStream.AuthenticateAsClient(服务器);
}
赶上(的AuthenticationException E)
{

Console.WriteLine(异常:{0},e.Message);
如果(e.InnerException!= NULL)
{
Console.WriteLine(内部异常:{0},e.InnerException.Message);
}
Console.WriteLine(验证失败 - 关闭连接。+ e.Message);
client.Close();
的回报;
}


解决方案

答案可以在找到 SslStream.AuthenticateAsClient方法说明部分:



< BLOCKQUOTE>

有关targetHost指定必须在服务器证书的名称相匹配的值。




如果您使用服务器谁是主体的证书是CN = localhost,则必须调用AuthenticateAsClient用localhost作为targetHost参数成功验证它在客户端。如果你想用CN =大卫-PC作为证书主题比你必须调用AuthenticateAsClient了David-PC作为targetHost。 SslStream通过匹配你打算连接(和你传递给AuthenticateAsClient)与证书中的主题从服务器接收的服务器名称检查服务器标识。这种做法是在运行服务器的机器名证书的主体的名称相匹配,并在客户端,您传递相同的主机名AuthenticateAsClient因为你已经用于打开(在这种情况下,与TcpClient的)的连接。



但也有其他条件成功建立服务器和客户端之间的SSL连接:传递给AuthenticateAsServer该证书必须有一个私钥,必须在客户机上的信任和不得有相关的使用建立SSL会话的任意键使用限制。



现在关系到你的代码示例,你的问题是关系到一代和证书的使用。




  • 您没有为您的证书,这样它不能被信任的发布者 - 这是原因在RemoteCertificateChainErrors例外。我建议创造发展的目的指定makecert的-r选项自签名证书。


  • 要被信任的证书必须是自签署的,并放置在Windows证书存储可信位置或必须签名链本已受信任的证书颁发机构进行联系。因此,而不是在-ss我的选择,这将使该证书在个人存储使用-ss根,将其放置在受信任的根证书颁发机构,它将你的机器上是可信的(从代码我假定您的客户端运行在同一台机器的服务器,并在其上生成的证书)上。


  • 如果您指定输出文件makecert将证书导出为。 CER但这种格式只包含公共密钥,而不是由服务器需要建立SSL连接的私钥。最简单的方法是阅读从Windows证书存储在服务器代码证书。 (您也可以从商店导出为其他格式,允许存储为这里所描述的私钥的导出的私钥证书和读取服务器代码的文件)。




您可以找到有关这里使用证书创建工具(Makecert.exe)



在结束您的代码需要进行以下更改运行(与你的最新代码更新测试):




  • 使用以下命令生成证书:




makecert -sr LOCALMACHINE -ss根-r -nCN = localhost的-sky交换
-sk 123456





  • 阅读从Windows证书存储区,而不是文件中的证书(在这个例子中的简单性),因此替换




serverCertificate = X509Certificate.CreateFromCertFile(证书);




在服务器代码:

 的X509Store店=新的X509Store(StoreName.Root,StoreLocation.LocalMachine); 
store.Open(OpenFlags.ReadOnly);
无功证书= store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName,CN = localhost的,假的);
store.Close();

如果(certificates.Count == 0)
{
Console.WriteLine(服务器证书没有找到......);
的回报;
}
,否则
{
serverCertificate =证书[0];
}

请记住与证书的主题来代替CN = localhost的是您打算如果以后更改代码使用(在这种情况下应该是相同的值传递给makecert -n选项)。也可以考虑使用运行服务器,而不是在服务器证书的主题本地主机的机器名称。


I have recently been trying to make a SSL encrypted Server/Client in C#.

I have followed this tutorial on MSDN, however, it required a certificate to be created for the server and client usage using makecert.exe so I found an example and it created the certificate fine:

makecert -sr LocalMachine -ss My -n "CN=Test" -sky exchange -sk 123456 c:/Test.cer

but now the problem is the server starts and waits for clients, when the client connects it uses the machine name which as far as I can gather is my IP in this case:

127.0.0.1

, and then it requires the servers name which must match the servers name on the certificate (Test.cer). I have tried multiple combinations (such as "Test" "LocalMachine","127.0.0.1" but cant seem to get the clients given server name to match thus allowing the connection. The error I get is:

Certificate error: RemoteCertificateNameMismatch, RemoteCertificateChainErrors Exception: the remote certificate is invalid according to the validation procedure

here is the code I'm using it differs from the MSDN example only in the fact that I assign the certificate path for the server in the app and the machine name and server name of the client too:

SslTcpServer.cs

using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.IO;

namespace Examples.System.Net
{
    public sealed class SslTcpServer
    {
        static X509Certificate serverCertificate = null;
        // The certificate parameter specifies the name of the file  
        // containing the machine certificate. 
        public static void RunServer(string certificate)
        {
            serverCertificate = X509Certificate.CreateFromCertFile(certificate);
            // Create a TCP/IP (IPv4) socket and listen for incoming connections.
            TcpListener listener = new TcpListener(IPAddress.Any, 8080);
            listener.Start();
            while (true)
            {
                Console.WriteLine("Waiting for a client to connect...");
                // Application blocks while waiting for an incoming connection. 
                // Type CNTL-C to terminate the server.
                TcpClient client = listener.AcceptTcpClient();
                ProcessClient(client);
            }
        }
        static void ProcessClient(TcpClient client)
        {
            // A client has connected. Create the  
            // SslStream using the client's network stream.
            SslStream sslStream = new SslStream(
                client.GetStream(), false);
            // Authenticate the server but don't require the client to authenticate. 
            try
            {
                sslStream.AuthenticateAsServer(serverCertificate,
                    false, SslProtocols.Tls, true);
                // Display the properties and settings for the authenticated stream.
                DisplaySecurityLevel(sslStream);
                DisplaySecurityServices(sslStream);
                DisplayCertificateInformation(sslStream);
                DisplayStreamProperties(sslStream);

                // Set timeouts for the read and write to 5 seconds.
                sslStream.ReadTimeout = 5000;
                sslStream.WriteTimeout = 5000;
                // Read a message from the client.   
                Console.WriteLine("Waiting for client message...");
                string messageData = ReadMessage(sslStream);
                Console.WriteLine("Received: {0}", messageData);

                // Write a message to the client. 
                byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");
                Console.WriteLine("Sending hello message.");
                sslStream.Write(message);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
                }
                Console.WriteLine("Authentication failed - closing the connection.");
                sslStream.Close();
                client.Close();
                return;
            }
            finally
            {
                // The client stream will be closed with the sslStream 
                // because we specified this behavior when creating 
                // the sslStream.
                sslStream.Close();
                client.Close();
            }
        }
        static string ReadMessage(SslStream sslStream)
        {
            // Read the  message sent by the client. 
            // The client signals the end of the message using the 
            // "<EOF>" marker.
            byte[] buffer = new byte[2048];
            StringBuilder messageData = new StringBuilder();
            int bytes = -1;
            do
            {
                // Read the client's test message.
                bytes = sslStream.Read(buffer, 0, buffer.Length);

                // Use Decoder class to convert from bytes to UTF8 
                // in case a character spans two buffers.
                Decoder decoder = Encoding.UTF8.GetDecoder();
                char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
                decoder.GetChars(buffer, 0, bytes, chars, 0);
                messageData.Append(chars);
                // Check for EOF or an empty message. 
                if (messageData.ToString().IndexOf("<EOF>") != -1)
                {
                    break;
                }
            } while (bytes != 0);

            return messageData.ToString();
        }
        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.");
            }
        }
        public static void Main(string[] args)
        {
            string certificate = "c:/Test.cer";
            SslTcpServer.RunServer(certificate);
        }
    }
}

SslTcpClient.cs

using System;
using System.Collections;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.IO;

namespace Examples.System.Net
{
    public class SslTcpClient
    {
        private static Hashtable certificateErrors = new Hashtable();

        // The following method is invoked by the RemoteCertificateValidationDelegate. 
        public static bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;

            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

            // Do not allow this client to communicate with unauthenticated servers. 
            return false;
        }
        public static void RunClient(string machineName, string serverName)
        {
            // Create a TCP/IP client socket. 
            // machineName is the host running the server application.
            TcpClient client = new TcpClient(machineName, 8080);
            Console.WriteLine("Client connected.");
            // Create an SSL stream that will close the client's stream.
            SslStream sslStream = new SslStream(
                client.GetStream(),
                false,
                new RemoteCertificateValidationCallback(ValidateServerCertificate),
                null
                );
            // The server name must match the name on the server certificate. 
            try
            {
                sslStream.AuthenticateAsClient(serverName);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
                }
                Console.WriteLine("Authentication failed - closing the connection.");
                client.Close();
                return;
            }
            // Encode a test message into a byte array. 
            // Signal the end of the message using the "<EOF>".
            byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
            // Send hello message to the server. 
            sslStream.Write(messsage);
            sslStream.Flush();
            // Read message from the server. 
            string serverMessage = ReadMessage(sslStream);
            Console.WriteLine("Server says: {0}", serverMessage);
            // Close the client connection.
            client.Close();
            Console.WriteLine("Client closed.");
        }
        static string ReadMessage(SslStream sslStream)
        {
            // Read the  message sent by the server. 
            // The end of the message is signaled using the 
            // "<EOF>" marker.
            byte[] buffer = new byte[2048];
            StringBuilder messageData = new StringBuilder();
            int bytes = -1;
            do
            {
                bytes = sslStream.Read(buffer, 0, buffer.Length);

                // Use Decoder class to convert from bytes to UTF8 
                // in case a character spans two buffers.
                Decoder decoder = Encoding.UTF8.GetDecoder();
                char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
                decoder.GetChars(buffer, 0, bytes, chars, 0);
                messageData.Append(chars);
                // Check for EOF. 
                if (messageData.ToString().IndexOf("<EOF>") != -1)
                {
                    break;
                }
            } while (bytes != 0);

            return messageData.ToString();
        }
        public static void Main(string[] args)
        {
            string serverCertificateName = null;
            string machineName = null;
            /*
            // User can specify the machine name and server name. 
            // Server name must match the name on the server's certificate. 
            machineName = args[0];
            if (args.Length < 2)
            {
                serverCertificateName = machineName;
            }
            else
            {
                serverCertificateName = args[1];
            }*/
            machineName = "127.0.0.1";
            serverCertificateName = "David-PC";// tried Test, LocalMachine and 127.0.0.1
            SslTcpClient.RunClient(machineName, serverCertificateName);
            Console.ReadKey();
        }
    }
}

EDIT:

The server accepts the clients connection and everything but it times out while waiting for the client to send a message. (The client wont authenticate with server due to the server name in the certificate being different from the one I supplied in the client) well thats my thoughts on it just to clarify

UPDATE:

According to an answer I have changed the certficiate maker to:

makecert -sr LocalMachine -ss My -n "CN=localhost" -sky exchange -sk 123456 c:/Test.cer and in my client I have:

        machineName = "127.0.0.1";
        serverCertificateName = "localhost";// tried Test, LocalMachine and 127.0.0.1
        SslTcpClient.RunClient(machineName, serverCertificateName);

now I get the exception:

RemoteCertificateChainErrors Exception: the remote certificate is invalid according to the validation procedure

which is occuring here:

  // The server name must match the name on the server certificate. 
            try
            {
                sslStream.AuthenticateAsClient(serverName);
            }
            catch (AuthenticationException e)
            {

                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
                }
                Console.WriteLine("Authentication failed - closing the connection. "+ e.Message);
                client.Close();
                return;
            }  

解决方案

The answer can be found at SslStream.AuthenticateAsClient Method Remarks section:

The value specified for targetHost must match the name on the server's certificate.

If you use for the server a certificate who's subject is "CN=localhost", you must call AuthenticateAsClient with "localhost" as targetHost parameter to successfully authenticate it on the client side. If you would use "CN=David-PC" as certificate subject than you must call AuthenticateAsClient with "David-PC" as targetHost. SslStream checks the the server identity by matching the server name that you intend to connect (and which you pass to AuthenticateAsClient) with the subject in the certificate received from the server. The practice is that the machine name that runs the server matches the name of the certificate's subject, and in the client you pass the same hostname to AuthenticateAsClient as you have used for opening the connection (with TcpClient in this case).

However there are other conditions to successfully establish SSL connection between servers and clients: the certificate passed to AuthenticateAsServer must have a private key, it must be trusted on the client machine and must not have any key usage restrictions related to usage for establishing SSL sessions.

Now related to your code sample, your problem is related to the generation and usage of the certificate.

  • You are not providing an issuer for your certificate and in this way it can't be trusted - this is the cause of the RemoteCertificateChainErrors Exception. I suggest to create a self signed certificate for development purposes specifying the -r option of makecert.

  • To be trusted a certificate must either be self-signed and placed in a trusted location in the Windows Certificate Store or must be linked with a chain of signatures to an already trusted Certificate Authority. So instead of the -ss My option which will place the certificate in the Personal store use -ss root that will place it in the Trusted Root Certification Authorities and it will be trusted on your machine (from the code I assume that your client is running on the same machine with the server and also the certificate is generated on it).

  • If you specify a output file to makecert it will export the certificate as .cer but this format contains only the public key, not the private key that is needed by the server to establish a SSL connection. The easiest way is to read the certificate from the Windows Certificate store in the server code. (You can also export it from the store in another format that allows storing the private key as described here Export a certificate with the private key and read that file in the server code).

You can find details about the makecert options used here Certificate Creation Tool (Makecert.exe)

In conclusion your code needs the following changes to run (tested with your latest code updates):

  • Use the following command to generate the certificate:

makecert -sr LocalMachine -ss root -r -n "CN=localhost" -sky exchange -sk 123456

  • Read the certificate from Windows Certificate Store instead of a file (for the simplicity of this example), so replace

serverCertificate = X509Certificate.CreateFromCertFile(certificate);

in the server code with:

X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, "CN=localhost", false);
store.Close();

if (certificates.Count == 0)
{
    Console.WriteLine("Server certificate not found...");
    return;
}
else
{
    serverCertificate = certificates[0];
}

Please remember to replace "CN=localhost" with the subject of the certificate that you intend to use if you change the code later (in this situation should be the same value as the -n option passed to makecert). Also consider to use the machine name that runs the server instead of localhost in the server certificate's subject.

这篇关于如何在C#中找出我的服务器名称服务器认证通过客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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