如何使用 TLS/SSL 保護通訊端連線 (HTML)
[ 本文的目標對象是撰寫 Windows 執行階段 App 的 Windows 8.x 和 Windows Phone 8.x 開發人員。如果您正在開發適用於 Windows 10 的 App,請參閱 最新文件 ]
本主題說明 在 Windows 市集應用程式中使用 StreamSocket 功能時,如何使用 TLS/SSL 保護資料流通訊端連線。
您必須知道的事
技術
-
使用通訊端與 WebSocket 啟用網路通訊。
先決條件
- 這個主題的下列範例是使用 JavaScript 撰寫的。建議對通訊端與 SSL/TLS 的使用有基本的了解。
SSL/TLS 連線概觀
安全通訊端層 (SSL) 與較新的傳輸層安全性 (TLS) 是密碼編譯通訊協定,其設計目的在於提供網路通訊的驗證與加密。這些通訊協定的設計目的在於防止傳送和接收網路資料時遭到竊取和竄改。 這些通訊協定使用用戶端伺服器模型以進行通訊協定交換。這些通訊協定也使用數位憑證與憑證授權單位,以驗證該伺服器是否為其本身所宣稱的伺服器。TLS 通訊協定記載於 IETF RFC 5246。 較舊的 SSL 通訊協定是由 Netscape Communications 所記載。 SSL 通常用來指這兩項通訊協定。
StreamSocket 物件可用來設定在用戶端與伺服器之間使用 SSL/TLS 進行通訊。對於 SSL/TLS 的支援受限於使用 StreamSocket 物件做為 SSL/TLS 交涉中的用戶端。收到在已建立的StreamSocket 上啟用 SSL/TLS 的連線時,SSL/TLS 目前無法在已建立 StreamSocket 的情況下由 StreamSocketListener 使用,因為並沒有為 StreamSocket.實作做為伺服器的 SSL/TLS 交涉。 SSL/TLS 的用戶端支援不包含使用用戶端憑證的能力。
有兩種方式可使用 SSL/TLS 保護 StreamSocket 連線:
- ConnectAsync - 建立連至網路服務的初始連線,並立即交涉讓所有通訊使用 SSL/TLS。
- UpgradeToSslAsync - 在不加密的情況下初次連線至網路服務。應用程式可能會傳送或接收資料。然後為所有進一步的通訊將連線升級成使用 SSL/TLS。
使用 ConnectAsync
建立具有網路服務的初始連線,然後為所有通訊立即交涉以使用 SSL/TLS。有兩種 ConnectAsync 方法可支援傳遞 protectionLevel 參數:
- ConnectAsync(EndpointPair, SocketProtectionLevel) - 在 StreamSocket 物件上啟動非同步操作,以連線至指定為 EndpointPair 物件與 SocketProtectionLevel 的遠端網路目的地。
- ConnectAsync(HostName, String, SocketProtectionLevel) - 在 StreamSocket 物件上啟動非同步操作,以連線至遠端主機名稱、遠端服務名稱以及 SocketProtectionLevel 所指定的遠端目的地。
如果在呼叫上面的 ConnectAsync 方法時,將 protectionLevel 參數設定為 Windows.Networking.Sockets.SocketProtectionLevel.Ssl,則 StreamSocket 必須使用 SSL/TLS 來加密。這個值需要加密而且絕不允許使用 NULL 密碼。
與其中一個 ConnectAsync 方法搭配使用的一般順序是相同的。
- 建立一個 StreamSocket。
- 如果需要通訊端上的進階選項,請使用 StreamSocket.Control 屬性取得與 StreamSocket 物件關聯的 StreamSocketControl 執行個體。 設定 StreamSocketControl 上的屬性。
- 呼叫上面其中一個 ConnectAsync 方法以啟動作業來連線至遠端目的地,並立即交涉以使用 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 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 - 啟動非同步操作以升級成在 StreamSocket 物件上使用 SSL。
UpgradeToSslAsync 方法需要兩個參數。protectionLevel 參數指示所需的保護層級。validationHostName 參數是在升級成 SSL 時用於驗證的遠端網路目的地的主機名稱。 通常 validationHostName 是應用程式用來初次建立連線的相同主機名稱。如果在呼叫上面的 UpgradeToSslAsync 方法時,將 protectionLevel 參數設定為 Windows.System.Socket.SocketProtectionLevel.Ssl,則 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 交涉根據伺服器數位憑證與憑證授權單位來驗證伺服器。
實際上使用 ConnectAsync 或 UpgradeToSslAsync 交涉的 SSL 強度可以透過取得 StreamSocketinformation.ProtectionLevel 屬性來決定。
相關主題
其他
參考
StreamSocket.UpgradeToSslAsync
StreamSocketinformation.ProtectionLevel
範例