引言
有个项目中用到了Socket ssl通信,在此记录一下.
证书
Socket ssl需要用到证书用来校验身份,而作为调试,我们只需用测试证书即可.
有个工具可以很方便地制作测试证书,下载地址为http://supersocket.codeplex.com/releases/view/59311
首先, 输入Common Name,密码和保存路径后,我们可以得到包含私钥的证书server.pfx.
然后,安装证书到电脑中,在IE选项中导出一份证书作为client.cer.
客户端
使用客户端的电脑需要安装client.cer到<受信任的根证书颁发机构>,且要把证书放在程序目录中,具体代码如下
class Program { private static SslStream _sslStream; static void Main(string[] args) { try { TcpClient client = new TcpClient("127.0.0.1", 6000); Console.WriteLine("Client connected."); _sslStream = new SslStream( client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null ); X509CertificateCollection certs = new X509CertificateCollection(); X509Certificate cert = X509Certificate.CreateFromCertFile(System.Environment.CurrentDirectory + @"" + "client.cer"); certs.Add(cert); //验证证书 try { _sslStream.AuthenticateAsClient("test", certs, SslProtocols.Tls, false); } 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(); Console.ReadLine(); return; } //开始读取消息 Task.Factory.StartNew(() => { ReadMessage(_sslStream); }); Console.WriteLine("按Q退出程序"); string message = ""; message = Console.ReadLine() + "<EOF>"; while (message != "Q") { byte[] bytes = Encoding.UTF8.GetBytes(message); _sslStream.Write(bytes); _sslStream.Flush(); Console.WriteLine("send:" + message); message = Console.ReadLine() + "<EOF>"; } client.Close(); } catch (Exception ex) { Console.WriteLine(ex); Console.ReadLine(); } } public static void ReadMessage(SslStream sslStream) { byte[] buffer = new byte[2048]; StringBuilder messageData = new StringBuilder(); int bytes = -1; do { bytes = sslStream.Read(buffer, 0, buffer.Length); Decoder decoder = Encoding.UTF8.GetDecoder(); char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)]; decoder.GetChars(buffer, 0, bytes, chars, 0); messageData.Append(chars); if (messageData.ToString().IndexOf("<EOF>", StringComparison.Ordinal) != -1) { break; } } while (bytes != 0); string message = messageData.ToString().Replace("<EOF>", ""); Console.WriteLine("recevied:" + message); ReadMessage(sslStream); } private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors) { if (sslpolicyerrors == SslPolicyErrors.None) return true; Console.WriteLine("Certificate error: {0}", sslpolicyerrors); return false; } }
服务端
服务端电脑要安装server.pfx证书,且要把证书放在程序目录中,具体代码如下
class Program { static void Main(string[] args) { TcpListener listener = new TcpListener(IPAddress.Any, 6000); listener.Start(); Console.WriteLine("Waiting for a client to connect..."); TcpClient client = listener.AcceptTcpClient(); _sslStream = new SslStream(client.GetStream(), true); try { serverCertificate = new X509Certificate(Environment.CurrentDirectory + @"" + "server.pfx", "1"); _sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls, true); } catch (Exception ex) { Console.WriteLine(ex); Console.ReadLine(); return; } while (true) { string receivedMessage = ReadMessage(_sslStream); Console.WriteLine("received:" + receivedMessage); byte[] message = Encoding.UTF8.GetBytes("Success.<EOF>"); _sslStream.Write(message); _sslStream.Flush(); } } static X509Certificate serverCertificate = null; private static SslStream _sslStream; static string ReadMessage(SslStream sslStream) { byte[] buffer = new byte[2048]; StringBuilder messageData = new StringBuilder(); int bytes = -1; do { bytes = sslStream.Read(buffer, 0, buffer.Length); Decoder decoder = Encoding.UTF8.GetDecoder(); char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)]; decoder.GetChars(buffer, 0, bytes, chars, 0); messageData.Append(chars); if (messageData.ToString().IndexOf("<EOF>") != -1) { break; } } while (bytes != 0); return messageData.ToString(); } static void ProcessClient(TcpClient client) { SslStream sslStream = new SslStream( client.GetStream(), true); try { sslStream.AuthenticateAsServer(serverCertificate, true, SslProtocols.Tls, true); Console.WriteLine("Waiting for client message..."); string messageData = ReadMessage(sslStream); Console.WriteLine("Received: {0}", messageData); byte[] message = Encoding.UTF8.GetBytes("已收到信息.<EOF>"); sslStream.Write(message); sslStream.Flush(); } 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 { sslStream.Close(); client.Close(); } } }
注意事项
1.服务端验证方法AuthenticateAsServer的参数clientCertificateRequired如果为true,那在客户端也要安装server.pfx.
2.客户端验证方法AuthenticateAsClient的参数targetHost对应证书中Common Name,也就是受颁发者.
参考资料
https://msdn.microsoft.com/zh-cn/library/system.net.security.sslstream(v=vs.110).aspx