[轉]如何藉助 TLS/SSL 確保套接字鏈接的安全(使用 C#/VB/C++ 和 XAML 的 Windows 應用商店應用)

本文轉自:http://msdn.microsoft.com/zh-cn/library/windows/apps/jj150597.aspxweb

本主題將展現在使用 StreamSocket 功能時,如何使 Windows 應用商店應用能夠保護 TLS/SSL 流套接字鏈接。windows

你須要瞭解的內容

技術

先決條件

  • 在本主題中,如下示例都來自 C# 和 C++。建議對套接字和 SSL/TLS 的使用有一個基本的瞭解。

SSL/TLS 鏈接概述

安全套接字層 (SSL) 和最新的傳輸層安全 (TLS) 都是旨在爲網絡通訊提供身份驗證和加密功能的加密協議。這些協議專門用於在發送和接收網絡數據時防止發生竊聽和篡改。 這些協議使用一種客戶端-服務器模型進行協議交換。這些協議還會使用數字證書和證書頒發機構來驗證服務器是否與其自稱的身份相符。TLS 協議記錄在 IETF RFC 5246 中。 早期的 SSL 協議由 Netscape Communications 記錄。 SSL 一般用於指這兩種協議。服務器

StreamSocket 對象能夠配置用於在客戶端和服務器之間使用 SSL/TLS 進行通訊。對 SSL/TLS 的支持僅限於在 SSL/TLS 協商中將 StreamSocket 對象用做客戶端。當系統接受一個鏈接以在被建立的 StreamSocket 上啓用 SSL/TLS 時,因爲做爲服務器的 SSL/TLS 協商沒有爲 StreamSocket 實現,因此StreamSocketListener 如今不能使用 SSL/TLS 用於被建立的 StreamSocket。 SSL/TLS 的客戶端支持不包括使用客戶端證書的功能。網絡

有如下兩種方法能夠藉助 SSL/TLS 確保 StreamSocket 鏈接的安全:app

 

  • ConnectAsync - 創建到網絡服務的初始鏈接並當即協商對全部通訊使用 SSL/TLS。
  • UpgradeToSslAsync - 先不加密鏈接到網絡服務。應用能夠發送或接收數據。而後升級鏈接,對此後全部通訊使用 SSL/TLS。

使用 ConnectAsync

創建與網絡服務的初始鏈接並當即協商對全部通訊使用 SSL/TLS。有兩種 ConnectAsync 方法支持傳遞protectionLevel 參數:異步

 

若是 protectionLevel 參數被設置爲 Windows.Networking.Sockets.SocketProtectionLevel.Ssl,當調用上述任一ConnectAsync 方法時,StreamSocket 必須使用 SSL/TLS 用於加密。此值須要加密並且毫不容許使用 NULL 密碼。socket

通常來講,使用這些 ConnectAsync 方法的順序都是相同的。async

 

如下示例將會建立 StreamSocket,並嘗試創建與網絡服務的鏈接並當即協商使用 SSL/TLS。若是協商成功,則在客戶端和網絡服務器之間使用 StreamSocket 的全部網絡通訊都將被加密。ide

 
using Windows.Networking;
using Windows.Networking.Sockets;


    // Define some variables and set values
    StreamSocket clientSocket = new StreamSocket();
 
    HostName serverHost = new HostName("www.contoso.com");
    string serverServiceName = "https";

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 

    // Try to connect to contoso using HTTPS (port 443)
    try {

        // Call ConnectAsync method with SSL
        await clientSocket.ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel.Ssl);

        NotifyUser("Connected");
    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }

        NotifyUser("Connect failed with error: " + exception.Message);
        // Could retry the connection, but for this simple example
        // just close the socket.

        clientSocket.Dispose();
        clientSocket = null; 
    }
       
    // Add code to send and receive data using the clientSocket
    // and then close the clientSocket



使用 UpgradeToSslAsync

先不加密創建與網絡服務的初始鏈接。應用能夠發送或接收數據。而後升級鏈接,對此後全部通訊使用 SSL/TLS。使用以下方法:

 

UpgradeToSslAsync 方法有兩個參數。protectionLevel 參數表示所需的保護級別。validationHostName 參數是在升級到 SSL 時用於進行驗證的遠程網絡目標的主機名。 一般狀況下,validationHostName 將是應用最初創建鏈接時所使用的相同主機名。若是 protectionLevel 參數被設置爲 Windows.System.Socket.SocketProtectionLevel.Ssl,當調用上述任一 UpgradeToSslAsync 方法時,StreamSocket 必須使用 SSL/TLS 用於加密。此值須要加密並且毫不容許使用 NULL 密碼。

通常來講,使用 UpgradeToSslAsync 方法的順序都是:

 

  • 建立一個 StreamSocket
  • 若是須要在套接字上使用高級選項,請使用 StreamSocket.Control 屬性獲取與 StreamSocket 對象 相關聯的StreamSocketControl 實例。 針對 StreamSocketControl 設置一個屬性。
  • 若是任何數據須要以不加密的形式進行發送和接收,則當即發送。
  • 調用 UpgradeToSslAsync 方法以啓動將鏈接升級爲使用 SSL/TLS。

如下示例將會建立 StreamSocket,並嘗試創建與網絡服務的鏈接、發送一些初始數據,而後協商使用 SSL/TLS。若是協商成功,則在客戶端和網絡服務器之間使用 StreamSocket 的全部網絡通訊都將被加密。

 
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

    // Define some variables and set values
    StreamSocket clientSocket = new StreamSocket();
 
    HostName serverHost = new HostName("www.contoso.com");
    string serverServiceName = "http";

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 

    // Try to connect to contoso using HTTP (port 80)
    try {
        // Call ConnectAsync method with a plain socket
        await clientSocket.ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel.PlainSocket);

        NotifyUser("Connected");

    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }

        NotifyUser("Connect failed with error: " + exception.Message, NotifyType.ErrorMessage);
        // Could retry the connection, but for this simple example
        // just close the socket.

        clientSocket.Dispose();
        clientSocket = null; 
        return;
    }

    // Now try to sent some data
    DataWriter writer = new DataWriter(clientSocket.OutputStream);
    string hello = "Hello World ☺ ";
    Int32 len = (int) writer.MeasureString(hello); // Gets the UTF-8 string length.
    writer.WriteInt32(len);
    writer.WriteString(hello);
    NotifyUser("Client: sending hello");

    try {
        // Call StoreAsync method to store the hello message
        await writer.StoreAsync();

        NotifyUser("Client: sent data");

        writer.DetachStream(); // Detach stream, if not, DataWriter destructor will close it.
    }
    catch (Exception exception) {
        NotifyUser("Store failed with error: " + exception.Message);
        // Could retry the store, but for this simple example
            // just close the socket.

            clientSocket.Dispose();
            clientSocket = null; 
            return;
    }

    // Now upgrade the client to use SSL
    try {
        // Try to upgrade to SSL
        await clientSocket.UpgradeToSslAsync(SocketProtectionLevel.Ssl, serverHost);

        NotifyUser("Client: upgrade to SSL completed");
           
        // Add code to send and receive data 
        // The close clientSocket when done
    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }

        NotifyUser("Upgrade to SSL failed with error: " + exception.Message);

        clientSocket.Dispose();
        clientSocket = null; 
        return;
    }
    


備註

SocketProtectionLevel 枚舉 有三個可能的值:

 

  • PlainSocket - 一個未加密的純套接字。
  • Ssl - 一個必須使用 SSL/TLS 進行加密的套接字。此值須要加密並且毫不容許使用 NULL 密碼。
  • SslAllowNullEncryption - 一個首選使用 SSL/TLS 進行加密的套接字。該值首選使用徹底加密,但容許基於服務器的配置使用 NULL 密碼(即不加密)。

一般不使用 SslAllowNullEncryption 值,由於它容許使用 NULL 密碼,這就意味着不加密,因此網絡通訊可能也不加密。SslAllowNullEncryption 值容許 SSL/TLS 協商,以根據服務器數字證書和證書頒發機構對服務器進行驗證。

若是使用 SslAllowNullEncryption 值,那麼實際上使用 ConnectAsync  UpgradeToSslAsync 協商獲得的 SSL 強度可經過獲取 StreamSocketinformation.ProtectionLevel 屬性來肯定。

相關主題

其餘
使用套接字進行鏈接
使用套接字進行鏈接(使用 JavaScript 和 HTML 的 Windows 應用商店應用)
如何藉助 TLS/SSL 確保套接字鏈接的安全(使用 JavaScript 和 HTML 的 Windows 應用商店應用)
如何使用高級套接字控件
快速入門:使用流套接字進行鏈接
參考
SocketProtectionLevel
StreamSocket
StreamSocket.ConnectAsync
StreamSocket.UpgradeToSslAsync
StreamSocketinformation.ProtectionLevel
Windows.Networking.Sockets
相關文章
相關標籤/搜索