引言
有個項目中用到了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