En las ultimas versiones de Windows 11 y Windows Server 2025, si lanzamos un archivo .rdp para conexión remota nos pedirá permisos para conectar devices. De momento este aviso se puede quitar mediante:
pero esto no garantiza que en próximas versiones funcione.
Lo que realmente pide es que el rdp esté firmado. Por ello, debemos firmar los .rdp con rdpsign:
Ejemplos de uso:
Después de ejecutarlo, abrir gpedit.msc e ir a:
Configuración de usuario > Plantillas administrativas > Componentes de Windows > Servicios de Escritorio remoto > Cliente de conexión a Escritorio remoto > Specify SHA1 thumbprints of certificates representing trusted .rdp publishers
Se habilita y pegar el thumbprint que te imprimió el script. Si se quisiera que sirviera para todos los usuarios del equipo, usar la misma policy en Configuración del equipo.
Este script crea un certificado nuevo en `CurrentUser\My` por defecto, usa `CodeSigningCert`, exporta además el `.cer` público, valida `rdpsign` con `/l` antes de tocar todo el lote y luego firma cada `.rdp` por separado creando `.bak` salvo que le digamos lo contrario. `rdpsign` sobrescribe el archivo firmado, no acepta comodines, y Microsoft indica que el thumbprint debe sacarse del almacén de certificados y usarse sin espacios.
Cuando acabe, quedarse con el `Thumbprint SHA1` que imprime. Ese es el valor que se debe pegar en la directiva **“Specify SHA1 thumbprints of certificates representing trusted .rdp publishers”**. Microsoft documenta que esa policy existe tanto en **User Configuration** como en **Computer Configuration**, y que si la huella coincide, el usuario no recibe warnings al abrir el `.rdp`.
Si al ser autofirmado se quiere dejar además confiado en Windows, Microsoft indica que los certificados autofirmados deben estar en el **Trusted Root Certificates store**, e `Import-Certificate` permite importar el `.cer` al store que elijas con `-CertStoreLocation`.
Detalle importante: cada ejecución de este script crea un certificado distinto, así que si se vuelve a lanzar más adelante se tendrá también un **thumbprint nuevo** y hay que actualizar esa policy con la nueva huella.
Código:
reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\Client" /v RedirectionWarningDialogVersion /t REG_DWORD /d 1 /f
Lo que realmente pide es que el rdp esté firmado. Por ello, debemos firmar los .rdp con rdpsign:
Código:
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Folder,
[ValidateSet('CurrentUser','LocalMachine')]
[string]$StoreScope = 'CurrentUser',
[ValidateRange(1,10)]
[int]$YearsValid = 3,
[ValidateNotNullOrEmpty()]
[string]$SubjectPrefix = 'RDP Publisher - SoloEstePC',
[switch]$TestOnly,
[switch]$NoBackup,
[switch]$OverwriteExistingBackups
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Test-IsAdministrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Throw-IfFalse {
param(
[bool]$Condition,
[string]$Message
)
if (-not $Condition) {
throw $Message
}
}
function Test-RdpSignProbe {
param(
[Parameter(Mandatory = $true)]
[string]$RdpsignPath,
[Parameter(Mandatory = $true)]
[string]$Thumbprint,
[Parameter(Mandatory = $true)]
[string]$FilePath
)
$text = (& $RdpsignPath /sha256 $Thumbprint /l $FilePath 2>&1 | Out-String)
$exitCode = $LASTEXITCODE
$badPatterns = @(
'could not be signed',
'Unable locate the certificate specified',
'A certificate needs to be specified'
)
$bad = $false
foreach ($pattern in $badPatterns) {
if ($text -match $pattern) {
$bad = $true
break
}
}
[PSCustomObject]@{
Ok = ($exitCode -eq 0 -and -not $bad)
ExitCode = $exitCode
Output = $text.Trim()
}
}
# 1) Comprobaciones de entorno
$requiredCommands = @(
'rdpsign.exe',
'New-SelfSignedCertificate',
'Export-Certificate',
'Get-FileHash'
)
foreach ($cmd in $requiredCommands) {
try {
$null = Get-Command $cmd -ErrorAction Stop
}
catch {
throw "No encuentro '$cmd' en este equipo."
}
}
if ($StoreScope -eq 'LocalMachine' -and -not (Test-IsAdministrator)) {
throw "Con -StoreScope LocalMachine debes ejecutar PowerShell como administrador."
}
try {
$resolved = Resolve-Path -LiteralPath $Folder -ErrorAction Stop
}
catch {
throw "La carpeta '$Folder' no existe o no es accesible."
}
Throw-IfFalse (@($resolved).Count -eq 1) "La ruta '$Folder' debe resolver a una sola carpeta."
$Folder = $resolved[0].ProviderPath
Throw-IfFalse ((Test-Path -LiteralPath $Folder -PathType Container)) "'$Folder' no es una carpeta."
# 2) Comprobar que hay .rdp
$rdpFiles = Get-ChildItem -LiteralPath $Folder -Filter '*.rdp' -File | Sort-Object Name
Throw-IfFalse ($rdpFiles.Count -gt 0) "No hay archivos .rdp en '$Folder'."
# 3) Comprobar escritura en la carpeta
try {
$writeProbe = Join-Path $Folder ([System.IO.Path]::GetRandomFileName())
Set-Content -LiteralPath $writeProbe -Value '' -NoNewline -ErrorAction Stop
Remove-Item -LiteralPath $writeProbe -Force -ErrorAction Stop
}
catch {
throw "No tengo permiso de escritura en '$Folder'."
}
# 4) Comprobar backups existentes
if (-not $TestOnly -and -not $NoBackup -and -not $OverwriteExistingBackups) {
$existingBackups = foreach ($file in $rdpFiles) {
$bak = $file.FullName + '.bak'
if (Test-Path -LiteralPath $bak) {
$bak
}
}
if ($existingBackups) {
$list = ($existingBackups | Sort-Object | ForEach-Object { " - $_" }) -join [Environment]::NewLine
throw "Ya existen backups .bak. Borralos o relanza con -OverwriteExistingBackups.`n$list"
}
}
# 5) Crear SIEMPRE un certificado NUEVO
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$subject = "CN=$SubjectPrefix $stamp"
$friendly = "RDP Signer $env:COMPUTERNAME $stamp"
$storePath = "Cert:\$StoreScope\My"
$cert = New-SelfSignedCertificate `
-Type CodeSigningCert `
-Subject $subject `
-FriendlyName $friendly `
-CertStoreLocation $storePath `
-KeySpec Signature `
-KeyAlgorithm RSA `
-KeyLength 2048 `
-HashAlgorithm SHA256 `
-KeyExportPolicy NonExportable `
-NotAfter (Get-Date).AddYears($YearsValid)
Throw-IfFalse ($null -ne $cert) "No se pudo crear el certificado."
Throw-IfFalse ($cert.HasPrivateKey) "El certificado se creó sin clave privada."
Throw-IfFalse ($cert.NotAfter -gt (Get-Date)) "El certificado ya aparece caducado."
# 6) Usar el Thumbprint del store, sin espacios
$thumb = $cert.Thumbprint.Replace(' ', '').ToUpperInvariant()
# 7) Exportar CER publico (sin clave privada) por si luego quieres confiarlo/importarlo
$cerPath = Join-Path $Folder ("RdpPublisher-{0}-{1}.cer" -f $thumb.Substring(0,8), $stamp)
Export-Certificate -Cert $cert -FilePath $cerPath | Out-Null
# 8) Probar rdpsign contra el primer archivo antes de tocar todo el lote
$probe = Test-RdpSignProbe -RdpsignPath (Get-Command rdpsign.exe).Source -Thumbprint $thumb -FilePath $rdpFiles[0].FullName
if (-not $probe.Ok) {
throw "La prueba de rdpsign ha fallado.`nExitCode: $($probe.ExitCode)`n$($probe.Output)"
}
# 9) Firmar
$rdpsignPath = (Get-Command rdpsign.exe).Source
$results = @()
foreach ($file in $rdpFiles) {
$beforeHash = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256).Hash
$bakPath = $file.FullName + '.bak'
if (-not $TestOnly -and -not $NoBackup) {
if ($OverwriteExistingBackups) {
Copy-Item -LiteralPath $file.FullName -Destination $bakPath -Force
}
else {
Copy-Item -LiteralPath $file.FullName -Destination $bakPath
}
}
$args = @('/v', '/sha256', $thumb)
if ($TestOnly) {
$args += '/l'
}
$args += $file.FullName
$text = (& $rdpsignPath @args 2>&1 | Out-String)
$exitCode = $LASTEXITCODE
$bad = $text -match 'could not be signed|Unable locate the certificate specified|A certificate needs to be specified'
if ($exitCode -ne 0 -or $bad) {
throw "Error firmando '$($file.FullName)'.`nExitCode: $exitCode`n$text"
}
$afterHash = if ($TestOnly) {
$beforeHash
}
else {
(Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256).Hash
}
$results += [PSCustomObject]@{
File = $file.FullName
Backup = if ($TestOnly -or $NoBackup) { $null } else { $bakPath }
Mode = if ($TestOnly) { 'TestOnly (/l)' } else { 'Signed' }
Changed = if ($TestOnly) { $null } else { ($beforeHash -ne $afterHash) }
}
}
# 10) Resumen
Write-Host ''
Write-Host '================ RESUMEN ================'
Write-Host "Carpeta : $Folder"
Write-Host "Store certificado : $storePath"
Write-Host "Cert path : $storePath\$thumb"
Write-Host "Subject : $($cert.Subject)"
Write-Host "Thumbprint SHA1 : $thumb"
Write-Host "CER publico : $cerPath"
Write-Host "Archivos procesados: $($results.Count)"
Write-Host "Modo : $(if ($TestOnly) { 'Prueba (/l)' } else { 'Firmado real' })"
Write-Host ''
Write-Host 'Pega este Thumbprint en la policy de trusted .rdp publishers:'
Write-Host $thumb
Write-Host '========================================='
Write-Host ''
$results
Código:
# Prueba: crea un cert NUEVO y valida la firma, pero no toca los .rdp .\New-RdpPublisherAndSign.ps1 -Folder "C:\carpeta_donde_esten_los_rdp" -TestOnly
Código:
# Firma real, creando .bak .\New-RdpPublisherAndSign.ps1 -Folder "C:\carpeta_donde_esten_los_rdp"
Código:
# Firma real sin backups .\New-RdpPublisherAndSign.ps1 -Folder "C:\carpeta_donde_esten_los_rdp" -NoBackup
Configuración de usuario > Plantillas administrativas > Componentes de Windows > Servicios de Escritorio remoto > Cliente de conexión a Escritorio remoto > Specify SHA1 thumbprints of certificates representing trusted .rdp publishers
Se habilita y pegar el thumbprint que te imprimió el script. Si se quisiera que sirviera para todos los usuarios del equipo, usar la misma policy en Configuración del equipo.
Este script crea un certificado nuevo en `CurrentUser\My` por defecto, usa `CodeSigningCert`, exporta además el `.cer` público, valida `rdpsign` con `/l` antes de tocar todo el lote y luego firma cada `.rdp` por separado creando `.bak` salvo que le digamos lo contrario. `rdpsign` sobrescribe el archivo firmado, no acepta comodines, y Microsoft indica que el thumbprint debe sacarse del almacén de certificados y usarse sin espacios.
Cuando acabe, quedarse con el `Thumbprint SHA1` que imprime. Ese es el valor que se debe pegar en la directiva **“Specify SHA1 thumbprints of certificates representing trusted .rdp publishers”**. Microsoft documenta que esa policy existe tanto en **User Configuration** como en **Computer Configuration**, y que si la huella coincide, el usuario no recibe warnings al abrir el `.rdp`.
Si al ser autofirmado se quiere dejar además confiado en Windows, Microsoft indica que los certificados autofirmados deben estar en el **Trusted Root Certificates store**, e `Import-Certificate` permite importar el `.cer` al store que elijas con `-CertStoreLocation`.
Detalle importante: cada ejecución de este script crea un certificado distinto, así que si se vuelve a lanzar más adelante se tendrá también un **thumbprint nuevo** y hay que actualizar esa policy con la nueva huella.
Comentario