SSL編程(2).NET最簡單的客戶端

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對象的狀態以下:

p_w_picpath

代碼運行的結果以下:

p_w_picpath

咱們看到了服務器經過SSL安全通道返回的HTML網頁數據。