[转]如何借助 TLS/SSL 确保套接字连接的安全?使用 C#/VB/C++ 和 XAML 的 Windows 应用商店应用

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

本主题将展示在使用 StreamSocket 功能时,如何使 Windows 应用商店应用可以保护 TLS/SSL 流套接字连接。

你需要了解的内容

技术

先决条件

  • 在本主题中,以下示例都来自 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 连接的安全:

  • ConnectAsync - 建立到网络服务的初始连接并立即协商对所有通信使用 SSL/TLS。
  • UpgradeToSslAsync - 先不加密连接到网络服务。应用可以发送或接收数据。然后升级连接,对此后所有通信使用 SSL/TLS。

使用 ConnectAsync

建立与网络服务的初始连接并立即协商对所有通信使用 SSL/TLS。有两种 ConnectAsync 方法支持传递protectionLevel 参数:

如果 protectionLevel 参数被设置为 Windows.Networking.Sockets.SocketProtectionLevel.Ssl,当调用上述任一ConnectAsync 方法时,StreamSocket 必须使用 SSL/TLS 用于加密。此值需要加密而且绝不允许使用 NULL 密码。

一般来说,使用这些 ConnectAsync 方法的顺序都是相同的。

以下示例将会创建 StreamSocket,并尝试建立与网络服务的连接并立即协商使用 SSL/TLS。如果协商成功,则在客户端和网络服务器之间使用 StreamSocket 的所有网络通信都将被加密。

C#

C++

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,并尝试建立与网络服务的连接、发送一些初始数据,然后协商使用 SSL/TLS。如果协商成功,则在客户端和网络服务器之间使用 StreamSocket 的所有网络通信都将被加密。

C#

C++

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 值,那么实际上使用 ConnectAsyncUpgradeToSslAsync 协商得到的 SSL 强度可通过获取 StreamSocketinformation.ProtectionLevel 属性来确定。

相关主题

其他
使用套接字进行连接
使用套接字进行连接(使用 JavaScript 和 HTML 的 Windows 应用商店应用)
如何借助 TLS/SSL 确保套接字连接的安全(使用 JavaScript 和 HTML 的 Windows 应用商店应用)
如何使用高级套接字控件
快速入门:使用流套接字进行连接
参考
SocketProtectionLevel
StreamSocket
StreamSocket.ConnectAsync
StreamSocket.UpgradeToSslAsync
StreamSocketinformation.ProtectionLevel
Windows.Networking.Sockets