SSL編程(2).NET最簡單的客戶端 html
在Windows平臺上,實現SSL的.NET類是System.Net.Security.SslStream類。這個類既能夠用於創建SSL服務,也能夠用來做爲SSL客戶端鏈接遠端的SSL服務。編程
最簡單的SSL客戶端是鏈接一個無需客戶端驗證的服務器,客戶端沒必要出示數字證書。對公衆提供服務的門戶網站的https入口通常都是如此,檢驗服 務器證書的有效性是客戶端本身的事情。瀏覽器本身是一個典型的SSL客戶端,因爲客戶端要負責驗證服務器證書,因此使用可信的瀏覽器對於安全上網十分重 要。SSL服務器端網上很好找,隨便選一個https網站做爲對端,咱們的客戶端演示程序就可以創建一個實際的SSL安全鏈接了。瀏覽器
這裏,咱們的客戶端程序鏈接一個擁有公網數字證書的https網站,如https: //www.alipay.com。默認的系統安全配置會檢驗服務器給出的證書,決定安全協商的結果。若是遠端數字證書有 效,SslStream.AuthenticateAsClient函數調用會成功,不拋出任何異常。這個函數的名稱不是很準確,看起來像是隻作了鑑別工 做,實際上這個函數若是正常完成,意味着整個SSL握手工做的完成,包括鑑別和密鑰交換。應用程序隨後就能夠用這個SslStream對象的讀寫函數與遠 端服務器安全通訊,如請求一個網頁等等,服務器發送帳單,用戶提交用戶信用卡號等敏感信息都應該在這樣的安全鏈接中進行。安全
這裏咱們先創建一個普通的TCP鏈接,而後在基於這個TCP鏈接的通訊通道,創建SSL安全鏈接。這裏咱們不討論普通TCP鏈接的創建和使用問題。服務器
咱們的例子代碼的功能是,做爲ssl客戶端,鏈接一個知名的網站的https服務的端口。這個端口通常是443。ssl握手鑑別成功以後,咱們在 ssl通道上給服務器發送一個http頁面請求,而後接收服務器應答的html頁面數據,把頁面源碼直接以字符形式打印在控制檯上。感興趣的讀者能夠進一 步把它保存成html文件,用瀏覽器打開查看返回的網頁。tcp
代碼的簡要流程以下:ide
1 創建普通tcp鏈接。函數
2 構造sslstream對象post
3 在try塊中,調用authenticateasclient開始ssl握手過程,試圖與也服務器創建ssl鏈接。網站
4 安全地 收發加密的數據,或者 在catch塊中處理ssl握手異常。
代碼全文以下:
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(); // 驗證服務器證書的回調函數。 public static bool ValidateServerCertificate( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { if (sslPolicyErrors == SslPolicyErrors.None) return true; Console.WriteLine("Certificate error: {0}", sslPolicyErrors); // 不能經過的服務器證書驗證的,返回false,阻止創建鏈接。 return false; } public static void RunClient(string machineName, string serverName) {
// 1 創建普通tcp鏈接。 TcpClient client = new TcpClient(machineName, 443); Console.WriteLine("Client connected."); // 2 構造SslStream類的對象. SslStream sslStream = new SslStream( client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null ); // The server name must match the name on the server certificate. try { // 3 發起ssl握手,試圖創建ssl鏈接.
sslStream.AuthenticateAsClient(serverName, null, 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(); return; } // Encode a test message into a byte array. // Signal the end of the message using the "<EOF>". byte[] messsage = Encoding.UTF8.GetBytes("GET " + machineName + "/ HTTP/1.0\r\n"); // 發送ssl加密數據。. 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 { //接收來自服務器的Ssl加密保護的數據。
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); } while (bytes != 0); return messageData.ToString(); } private static void DisplayUsage() { Console.WriteLine("To start the client specify:"); Console.WriteLine("clientSync machineName [serverName]"); Environment.Exit(1); } public static int Main(string[] args) { string serverCertificateName = "www.taobao.com"; string machineName = "www.taobao.com"; SslTcpClient.RunClient(machineName, serverCertificateName); return 0; } } }
在調試器中,看到的sslStream對象的狀態以下:
代碼運行的結果以下:
咱們看到了服務器經過SSL安全通道返回的HTML網頁數據。