Skip to the content.

RDP certificate retrieval is different from retrieving a certificate from an HTTPS endpoint.

For HTTPS on port 443, the connection usually starts TLS immediately after the TCP connection is opened. RDP on port 3389 does not work that way. A client first sends an RDP negotiation request. If the server accepts a TLS-capable security mode, the client can then start the TLS handshake and inspect the server certificate.

TLSleuth now has an RdpTls transport for that flow.

Basic Usage

Use the default RDP port:

Get-TLSleuthCertificate -Hostname rdp.example.com -Port 3389 -Transport RdpTls

If -Transport is omitted, TLSleuth can infer RdpTls from port 3389:

Get-TLSleuthCertificate -Hostname rdp.example.com -Port 3389

Use an explicit transport when the service is on a custom port:

Get-TLSleuthCertificate -Hostname rdp.example.com -Port 3390 -Transport RdpTls

When connecting by IP address, use -TargetHost for SNI and certificate-name validation:

Get-TLSleuthCertificate `
  -Hostname 192.0.2.10 `
  -Port 3389 `
  -Transport RdpTls `
  -TargetHost rdp.example.com

You can also test runtime-available TLS protocol versions against the RDP transport:

Test-TLSleuthProtocol -Hostname rdp.example.com -Port 3389 -Transport RdpTls |
  Select Protocol, ConnectionSuccessful, NegotiatedProtocol, ErrorMessage

Why RDP Needs A Transport

TLSleuth’s transport model separates TCP connection setup, protocol-specific pre-TLS negotiation, TLS handshake, and result mapping.

For RDP, the pre-TLS step is binary protocol negotiation. TLSleuth sends an RDP Negotiation Request that asks for TLS-capable RDP security protocols:

If the server accepts one of those modes, TLSleuth starts the normal .NET SslStream handshake on the same network stream and extracts the certificate and negotiated TLS details.

If the server rejects the negotiation before TLS starts, there is no TLS certificate available through that RDP path.

Internal Flow

The public command flow stays the same shape as other TLSleuth transports:

flowchart TD
    Command["Get-TLSleuthCertificate"]
    Context["New-TlsOperationContext"]
    Connect["New-TlsConnectionContext"]
    Dispatch["Invoke-TlsTransportNegotiation"]
    Adapter["Invoke-RdpTlsTransportAdapter"]
    RdpNegotiation["Invoke-RdpTlsNegotiation"]
    Request["New-RdpNegotiationRequest"]
    Send["Send-BinaryProtocolData"]
    Read["Read-RdpNegotiationResponse"]
    Handshake["Start-TlsHandshake"]
    Details["Get-TlsHandshakeDetails"]
    Certificate["Get-RemoteCertificate"]
    Result["TLSleuth.CertificateResult"]
    Cleanup["Close-NetworkResources"]

    Command --> Context
    Command --> Connect
    Command --> Dispatch
    Dispatch --> Adapter
    Adapter --> RdpNegotiation
    RdpNegotiation --> Request
    RdpNegotiation --> Send
    RdpNegotiation --> Read
    Command --> Handshake
    Command --> Details
    Command --> Certificate
    Command --> Result
    Command --> Cleanup

The RDP-specific code is isolated behind the transport adapter. Once RDP negotiation succeeds, the TLS handshake and certificate extraction use the same helpers as the other transports.

What The RDP Transport Adds

The RDP implementation adds focused private helpers:

The public commands continue to use the existing orchestration path:

That keeps RDP protocol work out of the public command bodies.

Common Failures

Connection Refused Or Timed Out

The TCP connection failed before RDP negotiation started.

Check the hostname, port, firewall rules, and whether RDP is enabled on the target server.

RDP Negotiation Rejected

The TCP connection succeeded, but the server returned an RDP negotiation failure before TLS started.

TLSleuth reports errors such as:

RDP server rejected TLS negotiation: HYBRID_REQUIRED_BY_SERVER (0x00000005).

The exact reason depends on the server response.

Server Selected Non-TLS RDP Security

TLSleuth requires a TLS-capable selected security mode. If the server selects standard RDP security instead, TLSleuth stops because there is no TLS certificate to retrieve.

TLS Handshake Failure

RDP negotiation succeeded, but the TLS handshake failed. That can happen because of TLS protocol policy, cipher support, runtime behavior, or certificate validation behavior.

Use Test-TLSleuthProtocol with -Transport RdpTls to see which TLS protocol attempts succeed or fail.

NLA Caveat

Network Level Authentication is an authentication layer used by many RDP deployments. TLSleuth does not authenticate to the RDP server and does not log on. It negotiates far enough to start TLS, retrieve the certificate, and report TLS/session details.

If server policy rejects negotiation before TLS starts, TLSleuth reports the negotiation failure instead of returning a certificate result.

Testing And Validation

The RDP transport is covered by unit tests for:

TLSleuth also includes an opt-in integration test for a real RDP endpoint. It is skipped by default so normal CI does not depend on a reachable RDP server.

To enable it:

$env:TLSLEUTH_RDP_HOST = 'rdp.example.com'
$env:TLSLEUTH_RDP_PORT = '3389'
$env:TLSLEUTH_RDP_TARGETHOST = 'rdp.example.com'
Invoke-Build IntegrationTest

The full validation command used for normal development remains:

Invoke-Build Validate -File .\TLSleuth.build.ps1

Summary

RdpTls lets TLSleuth retrieve certificates from RDP services without treating port 3389 like ordinary implicit TLS.

The flow is:

TCP connect -> RDP negotiation -> TLS handshake -> certificate extraction -> result object

That keeps the user experience consistent with the rest of TLSleuth while respecting the extra protocol step RDP requires before TLS can start.