如何使用 TLS/SSL 确保套接字连接的安全 (HTML)

[ 本文适用于编写 Windows 运行时应用的 Windows 8.x 和 Windows Phone 8.x 开发人员。如果你要针对 Windows 10 进行开发,请参阅 最新文档 ]

本主题介绍 在 Windows 应用商店应用中使用 StreamSocket 功能时如何借助 TLS/SSL 确保流套接字连接的安全。

你需要了解的内容

技术

先决条件

  • 在本主题中,以下示例都来自 JavaScript。建议对套接字和 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 的所有网络通信都将被加密。



    // Define some global variables that can be used from
    // multiple functions as needed 
    var clientSocket = null;
    var serverHostName = null;
    var serverServiceName = null;


    function openClient() {
        clientSocket = new Windows.Networking.Sockets.StreamSocket();
        // Try to connect to contoso using HTTPS (port 443)
        serverHostName = new Windows.Networking.HostName("www.contoso.com");
        serverServiceName = "https";

        // Call connectAsync method with SSL
        clientSocket.connectAsync(serverHostName, serverServiceName, Sockets.SocketProtectionLevel.Ssl).done(onClientAccept, onConnectError);
    }

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

    // If the client connection was accepted, display
    // a message to the user
    function onClientAccept() {
        socketSample.displayStatus("Client: connection completed.");
    }

    // The connection failed so display an error message
    // Could retry the connection, but for this simple example
    // just close the socket.
    function onConnectError(reason) {
       socketsSample.displayStatus(reason);
       clientSocket.close();
       clientSocket = null; 
    }

使用 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 的所有网络通信都将被加密。



    // Define some global variables that can be used from
    // multiple functions as needed
    var clientSocket = null;
    var serverHostName = null;
    var serverServiceName = null;

    function openClient() {
        clientSocket = new Windows.Networking.Sockets.StreamSocket();
        // Try to connect to contoso initially using HTTP
        serverHostName = new Windows.Networking.HostName("www.contoso.com");
        serverServiceName = "http";

        // Call ConnectAsync method to establish initial connection
        clientSocket.connectAsync(serverHostName, serverServiceName).done(onClientAccept, onConnectError);
    }

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

    // If the client connection was accepted, display
    // a message to the user
    function onClientAccept() {
        socketSample.displayStatus("Client: connection completed.");
        sendHello();
    }

    // The connection failed so display an error message
    // We could retry the connection, but for this simple example
    // we just close the socket.
    function onConnectError(reason) {
        socketsSample.displayStatus(reason);
        clientSocket.close();
        clientSocket = null; 
    }

    // Send some data in a simple format that is
    // the length of the string of data in a 4-byte integer
    // followed by the string
    function sendHello() {
        if (!clientSocket) {
            socketsSample.displayStatus("Client: you must connect the client before using it.");
            return;
        }
        var writer = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
        var string = "Hello World ☺ ";
        var len = writer.measureString(string); // Gets the UTF-8 string length.
        writer.writeInt32(len);
        writer.writeString(string);
        socketsSample.displayStatus("Client: sending hello.");
        writer.storeAsync().done(onStore, onSendError);
        writer.detachStream(); // Detach stream, if not, DataWriter destructor will close it.
    }

    function onStore() {
        socketsSample.displayStatus("Client: sent hello.");
        upgradeClient();
    }

    function onSendError(reason) {
        socketsSample.displayStatus(reason);
        clientSocket.close();
        clientSocket = null; 
    }

    function upgradeClient() {
        if (!clientSocket) {
            socketsSample.displayStatus("Client: you must connect the client before using it.");
            return;
        }
        var validationName = serverHostName;
        upgradeToSslAsync(SocketProtectionLevel.Ssl, serverHostName).done(onUpgradeAccept, onUpgradeError);
    }         

    // If upgrade to SSL was successful, display message to user
    function onUpgradeAccept() {
        socketSample.displayStatus("Client: upgrade to SSL completed.");
    }

    // The upgrade connection failed so display an error message
    // We could retry the upgrade possibly changing the validationHostname, 
    // but for this simple example we just close the socket.
    function onUpgradeError(reason) {
        socketsSample.displayStatus(reason);
        clientSocket.close();
        clientSocket = null; 
    }

备注

SocketProtectionLevel 枚举 有几个可能的值:

  • PlainSocket - 一个未加密的纯套接字。

  • Ssl - 一个必须使用 SSL/TLS 进行加密的套接字。此值需要加密而且绝不允许使用 NULL 密码。

    此值支持 SSL 3.0 和 TLS 1.0 协议以及系统上安装的所有加密密码(NULL 密码除外)。

  • SslAllowNullEncryption - 一个首选使用 SSL/TLS 进行加密的套接字。该值首选使用完全加密,但允许基于服务器的配置使用 NULL 密码(即不加密)。

  • BluetoothEncryptionAllowNullAuthentication - 一个首选使用加密,但允许基于目标服务器的配置使用 NULL 密码(即不加密)的布尔套接字。

  • BluetoothEncryptionWithAuthentication - 一个必须使用加密的布尔套接字。此值需要加密而且绝不允许使用 NULL 密码。

  • Ssl3AllowWeakEncryption - 一个必须使用 SSL 进行加密的 TCP 套接字。此值支持 SSL 3.0 协议以及系统上安装的所有加密密码(NULL 密码除外)。此值允许使用 RC4 以及其他认为不安全的弱密码。

  • Tls10 - 一个必须使用 SSL 进行加密的 TCP 套接字。此值支持 TLS 1.0 协议以及系统上安装的所有加密密码(RC4、其他弱密码和 NULL 密码除外)。

  • Tls11 - 一个必须使用 SSL 进行加密的 TCP 套接字。此值支持 TLS 1.1 和 TLS 1.0 协议以及系统上安装的所有加密密码(RC4、其他弱密码和 NULL 密码除外)。

  • Tls12 - 一个必须使用 SSL 进行加密的 TCP 套接字。此值支持 TLS 1.2、TLS 1.1 和 TLS 1.0 协议以及系统上安装的所有加密密码(RC4、其他弱密码和 NULL 密码除外)。

通常不使用 SslAllowNullEncryption 值,因为它允许使用 NULL 密码,这就意味着不加密,所以网络通信可能也不加密。SslAllowNullEncryption 值允许 SSL/TLS 协商,以根据服务器数字证书和证书颁发机构对服务器进行验证。

实际上使用 ConnectAsyncUpgradeToSslAsync 协商得到的 SSL 强度可通过获取 StreamSocketinformation.ProtectionLevel 属性来确定。

相关主题

其他

使用套接字进行连接

如何使用数据报套接字进行连接

如何使用流套接字进行连接

如何使用高级套接字控件

参考

SocketProtectionLevel

StreamSocket

StreamSocket.ConnectAsync

StreamSocket.UpgradeToSslAsync

StreamSocketinformation.ProtectionLevel

Windows.Networking.Sockets

示例

StreamSocket 示例