当服务器上只有 TLS 1.2 可用时,Powershell 在调用 AuthenticateAsClient 期间失败

Powershell failing during call to AuthenticateAsClient when only TLS 1.2 available on server

我正在尝试使用 Powershell 创建一个安全套接字,但它在调用 AuthenticateAsClient 期间失败并出现各种错误,例如

Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.

A call to SSPI failed, see inner exception.

实际错误似乎因我尝试连接的主机而异。假设您有一台已禁用 TLS 1.0 的可用服务器,则复制该问题所需的代码非常简单。注意我已经尝试了多种服务器版本,但以下是在 Windows Server 2019、默认配置和 Powershell 5.1 上测试的。

$hostName = "myserver.com"
$port = 443
$client = New-Object Net.Sockets.TcpClient
$client.Connect($hostName,$port)
$ssl = New-Object System.Net.Security.SslStream($client.GetStream())
try {
    $ssl.AuthenticateAsClient($hostName)
    "Host: $hostName"
    "Authenticated: $($ssl.IsAuthenticated)"
    "Algorithm: $($ssl.CipherAlgorithm) $($ssl.CipherStrength)"
    "Hash: $($ssl.HashAlgorithm) $($ssl.HashStrength)"
    "Key exchange algorithm: $($ssl.KeyExchangeAlgorithm) $($ssl.KeyExchangeStrength)"
    "SSL protocol: $($ssl.SslProtocol)"    
} catch {
    Write-Error $_
} finally {
    $client.close()
}

这里和互联网上有一个常见的建议,即设置可用协议以使用 TLS 1.2,如下所示:

[System.Net.ServicePointManager]::SecurityProtocol =[System.Net.SecurityProtocolType]'Tls, Tls11, Tls12, Tls13'

这意味着这将允许 dot net 自动协商服务器上最高可用的 TLS 版本,但它绝不会尝试协商高于 TLS 1.0 的版本。当旧协议版本在服务器上被禁用时,调用将失败。

如果此代码确实连接,它将显示用于连接的 TLS 版本,您可以确认您的服务器是否仍启用 TLS 1.0。

经过反复试验,我能得出的唯一结论是 Powershell 中存在一些与 TLS 协议相关的错误,该错误基本上忽略了全局设置的值。但是,如果在本地计算机上配置 TLS 协议,则有一个解决方案。只需按如下方式修改您的调用:

$ssl.AuthenticateAsClient( $HostAddress, $null, [System.Net.SecurityProtocolType]'Tls, Tls12', $false )   

直接在此调用的重载版本中列出您需要的任何 TLS 版本。现在它将按预期协商最高的。