未处于管理模式时,在 TLS 1.2 连接期间无法创建 SSL/TLS 安全通道错误

Could not create SSL/TLS secure channel error during TLS 1.2 connection when not in admin mode

过去两天我一直在寻找答案,但仍然不知道从哪里开始调查。

我有一个 C# .net 标准库和一个允许我从 REST api 获取数据的 powershell 脚本。 powershell 脚本如下所示。

$cert = Get-ChildItem -Path Cert:\LocalMachine\My\<Thumbprint>
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri “https://some.url.to.get.my.data” -Method Get -Certificate $cert

直到最近,这里提到的这个脚本和 C# 代码都对我有用(不是 运行 作为管理员)。现在给我带来麻烦并返回错误消息:

Invoke-WebRequest : The request was aborted: Could not create SSL/TLS secure channel.
At line:3 char:1
+ Invoke-WebRequest -Uri “https://some.url.to.get.my.data ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

我的 C# 应用程序中存在同样的问题。我设置了

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

我设置了 X509 证书(此处未显示)并使用

进行调用
HttpClient.GetStringAsync(string requestUri).Result;

通话挂起,永远不会 returns。

有趣的是,如果我是 运行 Powershell 或 Visual Studio 管理员模式,Powershell 和 C# 代码都可以正常工作。

在我的沮丧中,我尝试通过 wireshark 查看我的客户端和服务器之间的调用。通过比较工作呼叫和非工作呼叫之间的呼叫模式,我可以看出握手似乎工作正常。至少在最初直到服务器应该发送数据之前。客户端出于某种原因向服务器发送 [FIN, ACK] 调用,连接终止。

我欢迎您提出任何建议。

谢谢。

我在另一个 .NET 应用程序中遇到了类似的问题,请验证是否已设置以下注册表项,另请注意,根据您的 .NET 和/或操作系统版本,可能需要特定的补丁以获取更多信息,请参阅:https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls

$RegistryKeys = @(
    @{
        Path = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319"
        Name = "SystemDefaultTlsVersions"
        Value = "1"
        PropertyType = "DWord"
    }

    @{
        Path = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319"
        Name = "SchUseStrongCrypto"
        Value = "1"
        PropertyType = "DWord"
    }
    @{
        Path = "HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319"
        Name = "SystemDefaultTlsVersions"
        Value = "1"
        PropertyType = "DWord"
    }
    @{
        Path = "HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319"
        Name = "SchUseStrongCrypto"
        Value = "1"
        PropertyType = "DWord"
    }
    @{
        Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server"
        Name = "Enabled"
        Value = "1"
        PropertyType = "DWord"
    }
    @{
        Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server"
        Name = "DisabledByDefault"
        Value = "0"
        PropertyType = "DWord"
    }
    @{
        Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client"
        Name = "Enabled"
        Value = "1"
        PropertyType = "DWord"
    }
    @{
        Path = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client"
        Name = "DisabledByDefault"
        Value = "0"
        PropertyType = "DWord"
    }
)

一些用于测试的代码:

$ErrorActionPreference = 'Stop'
$VerbosePreference = 'Continue'

Foreach ($RegistryKey in $RegistryKeys) {

    # * Validate Path
    # This code will check if the Path exists.
    Write-Verbose "Processing '$($RegistryKey.Path)' '$($RegistryKey.Name)'"    
    Write-Verbose "Validating if path '$($RegistryKey.Path)' exists"
    If (-Not (Test-Path -Path $RegistryKey.Path)) {
        Write-Warning "Path '$($RegistryKey.Path)' doest not exist"
        Continue
    }
    Write-Verbose "Path '$($RegistryKey.Path)' exists"

    # * Validate Property
    # This code will check if the property exists.
    Write-Verbose "Reading properties for'$($RegistryKey.Path)'"        
    $Properties = Get-ItemProperty -Path $RegistryKey.Path
    Write-Verbose "Validating if property '$($RegistryKey.Name)' exists"
    If (-Not ($RegistryKey.Name -in ($Properties | Get-Member).Name)) {
        Write-Warning "Property '$($RegistryKey.Name)' doest not exist"
        Continue
    }
    Write-Verbose "Property '$($RegistryKey.Name)' exists"   

    # * Validate Property value
    # This code will check if the configured value is correct.
    Write-Verbose "Validating if property value is set to '$($RegistryKey.Value)'"
    If (-Not ((Get-ItemProperty -Path $RegistryKey.Path -Name $RegistryKey.Name)."$($RegistryKey.Name)" -eq $RegistryKey.Value)) {
        Write-Warning "Property value is incorrect for '$($RegistryKey.Path)' '$($RegistryKey.Name)'"
        Continue    
    }
    Write-Verbose "Property value is correct for '$($RegistryKey.Path)' '$($RegistryKey.Name)'"     
}

就我而言,事实证明问题确实出在获取证书上。我最初在开发过程中在当前用户帐户中拥有该证书,该证书可以正常工作。将证书移动到本地计算机帐户后,我开始遇到这个问题。

解决方案是通过证书管理器授予我的用户帐户访问特定证书的权限 > 右键单击​​证书 > 所有任务 > 管理私钥。然后将我的用户帐户添加到列表中。