Arreglar Load key: invalid format en Windows

Hace poco necesitaba conectarme desde Windows a un VPS usando SSH. El objetivo era entrar en la máquina para revisar unos procesos de seguridad que tengo automatizados, entre ellos ejecuciones periódicas de Prowler y WPScan.

La conexión SSH parecía sencilla: tenía la clave privada guardada en un gestor de contraseñas, la copié a Windows, la guardé en un archivo dentro de la carpeta .ssh y lancé el comando ssh.

Pero apareció este error:

Load key ".\vps_id_rsa": invalid format
root@IP_DEL_VPS's password:

En este post explico el proceso completo: cómo guardar la clave privada en Windows, cómo conectarse por SSH, qué significa el error invalid format, cómo diagnosticarlo y cómo lo solucioné reconstruyendo el archivo de clave en un formato limpio.

Contexto

El entorno era el siguiente:

  • Sistema local: Windows.
  • Terminal: PowerShell.
  • Servidor remoto: VPS
  • Usuario SSH: root.
  • Autenticación: clave privada SSH.
  • Archivo de clave esperado: vps_id_rsa.

La idea era poder conectarme así:

ssh -i .\vps_id_rsa root@IP_DEL_VPS

La clave privada estaba guardada previamente en un gestor de contraseñas. Al copiarla desde allí a un archivo local, algo en el formato del fichero quedó mal.


Crear la carpeta .ssh en Windows

Lo primero fue asegurar que existía la carpeta .ssh dentro del perfil de usuario de Windows:

mkdir $env:USERPROFILE\.ssh -Force

Después entré en esa carpeta:

cd $env:USERPROFILE\.ssh

Crear el archivo de clave privada

Para crear el archivo donde pegar la clave privada usé Notepad:

notepad .\vps_id_rsa

Dentro del archivo pegué la clave privada completa.

Una clave privada OpenSSH debería tener este aspecto:

-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----

Es importante que:

  • tenga la línea BEGIN;
  • tenga la línea END;
  • conserve los saltos de línea;
  • no tenga comillas;
  • no tenga espacios extraños al principio o al final;
  • no se copie como una única línea;
  • no se confunda con la clave pública.

La clave pública, en cambio, suele tener una sola línea parecida a esta:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... nombre

La clave privada nunca debe compartirse ni pegarse en chats, tickets o documentación pública.


Renombrar el archivo si Windows lo guarda como .txt

Un problema habitual en Windows es crear el archivo desde Notepad y acabar con algo como:

vps_id_rsa.txt

Aunque SSH puede usar un archivo terminado en .txt si se le indica la ruta exacta, preferí renombrarlo para evitar confusiones:

Rename-Item .\vps_id_rsa.txt vps_id_rsa

Para comprobar los archivos de la carpeta:

dir $env:USERPROFILE\.ssh

También hay que tener cuidado si Windows oculta extensiones, porque a veces el archivo puede acabar llamándose realmente:

vps_id_rsa.txt.txt

Primer intento de conexión SSH

El comando de conexión fue:

ssh -i .\vps_id_rsa root@IP_DEL_VPS

La primera vez apareció el mensaje normal de confianza del host:

This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Respondí:

yes

Eso añadió el host al archivo known_hosts de Windows.

Después apareció el problema real:

Warning: Permanently added 'IP_DEL_VPS' (ED25519) to the list of known hosts.
Load key ".\vps_id_rsa": invalid format
root@IP_DEL_VPS's password:

El mensaje importante era:

Load key ".\vps_id_rsa": invalid format

Esto significa que SSH no ha podido interpretar el archivo indicado como una clave privada válida.


Comprobar si la clave privada tiene formato válido

El siguiente paso fue pedirle a ssh-keygen que extrajera la clave pública a partir de la clave privada:

ssh-keygen -y -f .\vps_id_rsa

Si la clave privada es válida, este comando devuelve una línea que empieza por:

ssh-ed25519 ...

o:

ssh-rsa ...

En mi caso devolvía:

Load key ".\vps_id_rsa": invalid format

Eso confirmaba que el problema no era el VPS, sino el archivo local de la clave.


Comprobar la primera y última línea del archivo

Para no mostrar la clave privada completa, revisé solo la primera línea:

Get-Content .\vps_id_rsa -TotalCount 1

La salida era correcta:

-----BEGIN OPENSSH PRIVATE KEY-----

Después comprobé primera línea, última línea, número de líneas y tamaño del archivo:

$lines = Get-Content .\vps_id_rsa
"FIRST: $($lines[0])"
"LAST: $($lines[-1])"
"LINES: $($lines.Count)"
"SIZE: $((Get-Item .\vps_id_rsa).Length) bytes"

El resultado era algo parecido a:

FIRST: -----BEGIN OPENSSH PRIVATE KEY-----
LAST: -----END OPENSSH PRIVATE KEY-----
LINES: 7
SIZE: 424 bytes

A primera vista, parecía correcto. Una clave ed25519 puede ser relativamente corta, así que 7 líneas y unos cientos de bytes no eran necesariamente un problema.


Validar el bloque Base64 de la clave

Una clave privada OpenSSH tiene una cabecera, una parte en Base64 y un cierre. Para confirmar si el cuerpo de la clave era Base64 válido, usé este comando:

$body = (Get-Content .\vps_id_rsa | Select-Object -Skip 1 | Select-Object -SkipLast 1) -join ""
"BASE64_VALID: $($body -match '^[A-Za-z0-9+/=]+$')"
"BODY_LENGTH: $($body.Length)"
try {
  $bytes = [Convert]::FromBase64String($body)
  "DECODED_BYTES: $($bytes.Length)"
  "HEADER: $([System.Text.Encoding]::ASCII.GetString($bytes[0..13]))"
} catch {
  "BASE64_DECODE_ERROR"
}

La salida fue:

BASE64_VALID: True
BODY_LENGTH: 344
DECODED_BYTES: 258
HEADER: openssh-key-v1

Esto era una señal muy importante: el contenido interno sí parecía una clave OpenSSH real.

El problema no era que la clave fuese completamente incorrecta. El problema estaba en cómo había quedado escrito el archivo.


Reconstruir el archivo de clave privada en formato limpio

La solución fue reconstruir un nuevo archivo a partir del bloque Base64 válido, reescribiéndolo con saltos de línea limpios y codificación ASCII.

Ejecuté esto desde PowerShell dentro de la carpeta .ssh:

$lines = Get-Content .\vps_id_rsa
$body = ($lines | Select-Object -Skip 1 | Select-Object -SkipLast 1) -join ""
$wrapped = ($body -split "(.{70})" | Where-Object { $_ -ne "" }) -join "`n"
$content = "-----BEGIN OPENSSH PRIVATE KEY-----`n$wrapped`n-----END OPENSSH PRIVATE KEY-----`n"
[System.IO.File]::WriteAllText("$PWD\vps_id_rsa_fixed", $content, [System.Text.ASCIIEncoding]::new())

Esto creó un nuevo archivo:

vps_id_rsa_fixed

Después probé de nuevo:

ssh-keygen -y -f .\vps_id_rsa_fixed

Esta vez funcionó y devolvió la clave pública:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... vps_security_tools

Eso confirmaba que el nuevo archivo ya era interpretado correctamente por OpenSSH.


Conectarse usando la clave corregida

Con el archivo corregido, la conexión se hizo así:

ssh -i .\vps_id_rsa_fixed root@IP_DEL_VPS

Si la clave pública correspondiente está instalada en el VPS, dentro de:

/root/.ssh/authorized_keys

entonces el acceso debería funcionar sin pedir contraseña.


Dejar la clave corregida como definitiva

Una vez comprobado que vps_id_rsa_fixed funcionaba, se puede sustituir el archivo anterior:

Remove-Item .\vps_id_rsa
Rename-Item .\vps_id_rsa_fixed vps_id_rsa

A partir de ese momento, la conexión queda así:

ssh -i .\vps_id_rsa root@IP_DEL_VPS

¿Qué había pasado realmente?

El error no estaba en el VPS, ni en el usuario root, ni en el host remoto.

El problema era el archivo local de la clave privada.

La clave privada había sido copiada desde un gestor de contraseñas a Windows, pero el archivo resultante tenía algún problema de formato: saltos de línea, wrapping, codificación, caracteres invisibles o alguna representación que hacía que ssh-keygen y ssh no pudieran parsearlo correctamente.

Internamente, el bloque Base64 sí contenía una clave OpenSSH válida, como demostraba esta salida:

HEADER: openssh-key-v1

Pero el archivo como tal no estaba en un formato que OpenSSH aceptara.

Al reconstruirlo limpiamente desde el cuerpo Base64, el problema desapareció.


Diferencia entre clave privada y clave pública

Es importante recordar cómo funciona SSH:

Tu equipo Windows:
  clave privada

VPS:
  clave pública en authorized_keys

La clave privada se queda siempre en tu ordenador. Nunca debe subirse al VPS ni compartirse.

La clave pública es la que se instala en el servidor, normalmente en:

/root/.ssh/authorized_keys

si el usuario es root, o en:

/home/usuario/.ssh/authorized_keys

si se usa otro usuario.

Cuando se crea un VPS y se selecciona una SSH key, se instala la clave pública en el servidor. La clave privada debe conservarla el usuario localmente.


Comandos útiles de diagnóstico

Ver la primera línea del archivo

Get-Content .\vps_id_rsa -TotalCount 1

Ver primera línea, última línea, número de líneas y tamaño

$lines = Get-Content .\vps_id_rsa
"FIRST: $($lines[0])"
"LAST: $($lines[-1])"
"LINES: $($lines.Count)"
"SIZE: $((Get-Item .\vps_id_rsa).Length) bytes"

Validar si la clave privada puede generar su clave pública:

ssh-keygen -y -f .\vps_id_rsa

Conectarse al VPS:

ssh -i .\vps_id_rsa root@IP_DEL_VPS

Reconstruir el archivo a partir del bloque Base64:

$lines = Get-Content .\vps_id_rsa
$body = ($lines | Select-Object -Skip 1 | Select-Object -SkipLast 1) -join ""
$wrapped = ($body -split "(.{70})" | Where-Object { $_ -ne "" }) -join "`n"
$content = "-----BEGIN OPENSSH PRIVATE KEY-----`n$wrapped`n-----END OPENSSH PRIVATE KEY-----`n"
[System.IO.File]::WriteAllText("$PWD\vps_id_rsa_fixed", $content, [System.Text.ASCIIEncoding]::new())

Conclusión

El error:

Load key ".\vps_id_rsa": invalid format

no siempre significa que la clave sea incorrecta o que no corresponda con el servidor.

También puede significar que el archivo local está mal formateado, especialmente si la clave se ha copiado desde un gestor de contraseñas, una web, un correo o una nota.

En este caso, la clave era válida, pero el archivo no estaba en un formato aceptado por OpenSSH. La solución fue reconstruir el archivo limpiamente en PowerShell, validarlo con ssh-keygen y usarlo después para conectar al VPS.

La lección principal: antes de culpar al servidor, valida siempre la clave local con:

ssh-keygen -y -f .\vps_id_rsa

Si ese comando falla, el problema está en tu archivo de clave privada, no en el VPS.

Links

¿Te ha ayudado este artículo?

Invítame a un café

Leave a Reply

Your email address will not be published. Required fields are marked *