CorBlogкейсы · ops

Статья

RDP 0x204: сеть жива, а виноват TLS private key

macOS Windows App пишет 0x204, порт 3389 слушается — TermService не может открыть закрытый ключ сертификата RDP.

С Mac подключаюсь к Windows через Windows App и получаю Error code: 0x204. Порт 3389 слушается, firewall открыт, TCP доходит — а сессия сразу рвётся. Оказалось: не Mac и не сеть, а сломанный доступ TermService к private key TLS-сертификата RDP.

Что произошло

На macOS Windows App:

We couldn't connect to the remote PC.
Error code: 0x204

На Windows при этом:

  • RDP включён (fDenyTSConnections = 0);
  • 3389 в состоянии Listen у TermService;
  • в логе RdpCoreTS видно: TCP accepted → сразу terminated.

Как искал

Сначала отсёк очевидное: RDP включён, порт слушается, firewall пускает TCP 3389.

Главный сигнал — в System после неудачного коннекта:

Schannel 36870
A fatal error occurred when attempting to access the TLS server credential private key.
The SSPI client process is svchost[TermService].

Рядом:

TerminalServices-RemoteConnectionManager 1057
failed to create a new self signed certificate
Invalid algorithm specified

Иногда ещё 1059 — certificate configuration data was invalid.

Проверка привязки сертификата к RDP:

Get-CimInstance `
  -Namespace root/cimv2/terminalservices `
  -ClassName Win32_TSGeneralSetting `
  -Filter "TerminalName='RDP-tcp'" |
  Select-Object TerminalName,SSLCertificateSHA1Hash

По thumbprint сертификат в хранилище нашёлся, HasPrivateKey: True, но когда я реально открыл ключ:

$cert = Get-ChildItem Cert:\LocalMachine -Recurse |
  Where-Object Thumbprint -eq '<THUMBPRINT>' |
  Select-Object -First 1

[System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)

дало:

Keyset does not exist

То есть запись сертификата жива, а keyset уже нет или недоступен. Для RDP это фатально.

Решение

1. Новый сертификат в LocalMachine\My

New-SelfSignedCertificate нельзя класть сразу в Remote Desktop store — только в My.

$cert = New-SelfSignedCertificate `
  -CertStoreLocation Cert:\LocalMachine\My `
  -DnsName $env:COMPUTERNAME `
  -KeyAlgorithm RSA `
  -KeyLength 2048 `
  -HashAlgorithm SHA256 `
  -KeyExportPolicy Exportable `
  -NotAfter (Get-Date).AddYears(5) `
  -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1")

$thumb = $cert.Thumbprint
$thumb

2. Права NETWORK SERVICE на private key

TermService обычно крутится от NT AUTHORITY\NETWORK SERVICE.

$cert = Get-ChildItem Cert:\LocalMachine\My |
  Where-Object Thumbprint -eq $thumb |
  Select-Object -First 1

$rsa = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)

$keyPath = if ($rsa.GetType().FullName -eq 'System.Security.Cryptography.RSACng') {
  Join-Path $env:ProgramData "Microsoft\Crypto\Keys\$($rsa.Key.UniqueName)"
} else {
  $keyName = $cert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
  Join-Path $env:ProgramData "Microsoft\Crypto\RSA\MachineKeys\$keyName"
}

icacls $keyPath /grant "NT AUTHORITY\NETWORK SERVICE:R"

3. Привязать thumbprint к RDP-Tcp и перезапустить службу

На части сборок метода SetSSLCertificateSHA1Hash нет — работает присваивание через WMI:

$ts = Get-WmiObject `
  -Namespace root\cimv2\terminalservices `
  -Class Win32_TSGeneralSetting `
  -Filter "TerminalName='RDP-tcp'"

$ts.SSLCertificateSHA1Hash = $thumb
$ts.Put()

Restart-Service TermService -Force

Проверка:

Get-CimInstance `
  -Namespace root/cimv2/terminalservices `
  -ClassName Win32_TSGeneralSetting `
  -Filter "TerminalName='RDP-tcp'" |
  Select-Object TerminalName,SSLCertificateSHA1Hash

После удачного коннекта свежих Schannel 36870 и 1057/1059 быть не должно.

Итог

Если 0x204, TCP accepted, а потом сервер сам рвёт сессию и в System есть Schannel 36870 — почти наверняка виноват TLS/private key на стороне RDP, а не Mac-клиент.

Частые причины:

  • подчистили ключи в C:\ProgramData\Microsoft\Crypto;
  • слетели ACL;
  • клон или snapshot VM;
  • либо self-signed не пересоздался (Invalid algorithm specified).