Are you here because suddenly you’ve found that whenever you or your users open .RDP files, this security prompt is now shown:
You’ve investigated and you’ve found that this is due to an intentional security change Microsoft made in April 2026 updates that cause any unsigned .RDP file to present this warning on every launch as documented here:
Do you need a solution to prevent this warning specifically for when opening .RDP files generated via Remote Desktop Services (RDS)?
I found myself in this situation and at the time no clear end to end instructions so once I figured it out, I vowed to myself I’d shared with the community.
Background
Microsoft has supported signed .RDP files for as long as I can remember. What changed in the 2026 update is Microsoft will now show the security prompt above if no signature inside the .RDP file is present or if the signature presented was not issued by a root certificate already present in the Trusted Root Certificate authority. If you’re unfamiliar, a signature in this case is just a plain text “password” that exists inside the .RDP file that you can view simply by opening the file in notepad.
On a more technical level, what actually changed in the April 2026 update is Remote Desktop Connection now loads a list of thumbprints defined in a specific registry key and compares that to the thumbprint of the signer of the .RDP file. If they match then you’ve effectively said “I as the administrator have previously stated I officially trust any .RDP file issued by this certificate so you don’t have to warn me anymore.”
So how do you actually set this up?
Set up
In my environment we have a Remote Desktop Services server used for a handful of Remote Apps by about 100 people where all of the devices are off domain. They download the .RDP file from the Remote Desktop Web server and then launch them on demand. We have an RMM tool we use to push configuration to all devices.
The solution I came up with can be broken up into 5 steps:
1) Create self signed root and child certificates and use the child cert to sign all .rdp files published by the Remote Desktop Services server
2) Use RMM to deploy the root self signed public certificate to all machines trusted Root Certificates store
3) Use RMM to update the local computer’s registry to trust the self signed certificate for .RDP files
4) Configure Remote Desktop Services to sign files using the Self Signed certificate
5) Have users connect to Remote Desktop services to re-download new .RDP files signed with the self signed certificate now trusted by their machines
Below is the script I cobbled together to create the required self signed certificates.
This code will create root and child certificates in $SavePath. Put the root certificate in a safe location as it won’t be needed now but it will be when the child certificate expires and you need to renew it.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# Check for admin rights $IsAdmin = ([Security.Principal.WindowsPrincipal] ` [Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $IsAdmin) { Write-Error "This script must be run as Administrator. Exiting."; break } # Create Root certificate that will be valid for 15 years emulating the standard life time of other commercial root certificates Function New-RVSelfSignedRootCertificate($CertName, $RootCertPassword, $SavePath) { if(-not $CertName) { write-host "CertName missing"; break } if(-not $RootCertPassword) { write-host "RootCertPassword missing"; break } if(-not $SavePath) { write-host "SavePath missing. Provide path to save root cert to in the format of 'c:\temp'. Do not specify a filename."; break } $params = @{ 'Subject' = "CN=$CertName" 'KeyUsage' = @('CertSign', 'CRLSign', 'DigitalSignature') 'KeyExportPolicy' = 'Exportable' 'CertStoreLocation' ='Cert:\LocalMachine\My' 'NotAfter' = (Get-Date).AddYears(15) 'Type' = 'Custom' 'TextExtension' = @("2.5.29.19={critical}{text}ca=true") } $RootCert = New-SelfSignedCertificate @params # Set a password for the PFX export to use to import into RDS $pwd = ConvertTo-SecureString -String $RootCertPassword -Force -AsPlainText ## Export PFX of Root Certificate to store securely to be used to renew child certificate in future Export-PfxCertificate -Cert $RootCert -FilePath "$SavePath\rootcert.pfx" -Password $pwd | out-null $base64cert = [Convert]::ToBase64String($RootCert.Export('Cert')) $Base64cert | Out-file "$SavePath\publicroot.cer" #Return the Root Certificate so it can be passed into create a child cert if run at the same time write-host "$($RootCert.subject) certificate issued" $RootCert } Function Get-PFXThumbprint($PFXPath, $Pfxpass) { if (-not $pfxPath) { write-host "pfxpath not specified"; break } if (-not $pfxpass) { write-host "pfxpass not specified"; break } $password = ConvertTo-SecureString $PFXpass -AsPlainText -Force $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $cert.Import($pfxPath, $password, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet) $cert.Thumbprint } Function New-RVChildCertificate($DNSName, $SigningRootCert, $PFXPassword) { if(-not $DNSName) { write-host "DNSName missing"; break } if(-not $SigningRootCert) { write-host "SigningRootcert missing"; break } if(-not $PFXPassword) { write-host "PFXpassword missing"; break } $params = @{ 'DnsName' = "remote.mycompany.com" 'Subject' = "CN=remote.mycompany.com" 'Signer' = $SigningRootCert 'FriendlyName' = "My Company RDP Certificate" 'CertStoreLocation' = "cert:\LocalMachine\My" 'KeyUsage' = @('DigitalSignature', 'KeyEncipherment') 'Type' = 'Custom' 'TextExtension' = @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") 'KeyExportPolicy' = 'Exportable' 'NotAfter' = (Get-Date).AddYears(2) } $ChildCert = New-SelfSignedCertificate @params $pwd = ConvertTo-SecureString -String $PFXPassword -Force -AsPlainText Export-PfxCertificate -Cert $ChildCert -FilePath "$SavePath\rdscert.pfx" -Password $pwd | out-null write-host "Childcert exported" } write-host "This script is not designed to be executed directly but rather loaded into an IDE and run the functions as needed. Exiting."; break $SavePath = 'c:\temp\cert' $ThisRootCert = New-RVSelfSignedRootCertificate -CertName "My Company Root CA" -RootCertPassword 'PASS' -SavePath $SavePath if(-not $ThisRootCert) { $ThisRootCert = get-childitem cert:\localmachine\my | where {$_.subject -match 'My Company Root CA'} } New-RVChildCertificate -DNSName remote.dynms.com -SigningRootCert $ThisRootCert -PFXPassword 'PASS' get-pfxthumbprint C:\temp\cert\rdscert.pfx 'PASS' |
Now that you have the certificate, you’ll want to upload your public portion of the Root certificate to your RMM. In my case I’m using a Ninja Custom field along with the thumbprint you want to add to every machine to be trusted. Run this script against every machine in your environment and set up a task to run it against any new machines as they are added to your RMM to ensure everyone trusts your self signed root certificate and specifically trusts it for .RDP files.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
$PublicRootCert = ninja-property-get publicrootcert $RDSThumbprint = ninja-property-get rdsthumbprint if (-not $PublicRootCert) { Write-Host "Public Root cert not found in custom field called publicrootcert. Exiting."; return } if (-not $RDSThumbprint) { Write-Host "RDSThumbprint not found in custom field called rdsthumbprint. Exiting."; return } $RDSThumbprint = $RDSThumbprint.Trim().ToUpper() $tempFile = [System.IO.Path]::GetTempFileName() $PublicRootCert | Out-File $tempFile try { $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $cert.Import($tempFile) $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root","LocalMachine") $store.Open("ReadWrite") $existingCert = $store.Certificates | Where-Object { $_.Thumbprint -eq $cert.Thumbprint } $installCert = $true if ($existingCert) { if ($existingCert.NotAfter -gt (Get-Date)) { Write-Host "Certificate already exists and is not expired. Skipping install." $installCert = $false } else { Write-Host "Certificate exists but is expired. Replacing." $store.Remove($existingCert) } } if ($installCert) { Write-Host "Installing certificate." $store.Add($cert) } $store.Close() } finally { Remove-Item $tempFile -ErrorAction SilentlyContinue } # --- THUMBPRINT REGISTRY LOGIC --- $regPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" $valueName = "TrustedCertThumbprints" if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null } try { $existingThumbprints = (Get-ItemProperty -Path $regPath -Name $valueName -ErrorAction Stop).$valueName } catch { $existingThumbprints = $null } if (-not $existingThumbprints) { Write-Host "No existing thumbprints. Setting value." $updatedThumbprints = $RDSThumbprint } else { # Split into array and normalize $thumbprintList = $existingThumbprints.Split(",") | ForEach-Object { $_.Trim().ToUpper() } if ($thumbprintList -contains $RDSThumbprint) { Write-Host "Thumbprint already exists. No update needed." $updatedThumbprints = $existingThumbprints } else { Write-Host "Adding new thumbprint." $updatedThumbprints = ($thumbprintList + $RDSThumbprint) -join "," } } New-ItemProperty -Path $regPath -Name $valueName -Value $updatedThumbprints -PropertyType String -Force -Verbose |
Lastly, you’ll need to go into Remote Desktop Services / Deployment Settings / Certificates and update the RD Connection Broker – Publishing with the child PFX file you created above. Microsoft allows you to set a unique certificate just for publishing so this won’t interfere with your public certificate you use to access RDS.
If you’re wondering why we used the self signed certificate, it’s because I was unable to get this process to work as our commercial CA certificate did not support the “DigitalSignature” KeyUsage definition required for this process to work.
Summary
1) Create a self signed root certificate and then create a child PFX certificate based on that root certificate
2) Configure RDS to use that child PFX certificate for the RD Connection Broker – Publishing Role
3) Use a script to add the thumbprint of your certificate to the TrustedCertThumbprints key in HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services on each machine
4) Push out the public key of your root certificate to the trusted root certificates store on all machines
5) Have users download new .RDP files from your RDS server signed with your new self signed certificate
The users should now have the option to “don’t ask me again” on the certificate warning prompt.