Using SSH.NET
, I'm trying to get the same ed25519
and rsa
host keys from my SSH host (a Raspberry Pi in this case) that the Windows SSH client gets, so that I can compare them to what's in the %USERPROFILE%\.ssh\known_hosts
file.
The documentation demonstrates this technique, but e.FingerPrint
contains a 16-element byte array. I wish to replicate the functionality of the in-built Win11 SSH client that retrieves and stores the host keys like so:
offsite ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICuy9wsbY7fBFFoB6K0RU/BsISB3mL4PQ3311ta0S/cW
offsite ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDAEmXakR9rh5Sokj/PvmyiL/tZtQp7OPbQYb9PdlQjPXELJpBZkFs5gnoqxFj5ysqKnBREY/GpOKkXxdh5dMGGzoir93ypicI7A1VQTd3d8WQdtV1tnP7u9hbAfSqtj+7OGCsi0eMUBYx1fS3XbSC1jKr2MG2ovMCp3kdHtDoGrysP5ai0EXY07ZMmTpxTJbZFtZ8IX3U+ON71KIfZSC6UqlptFyFh1SUmpZXbnsbSr4BDoI8fG0A9Km9BTohWK9ioPSLL530KXz72S4EaBgx0Za1c+TGPqnjAu+s9kc0DeBrwLTEZzfS40xj+sdM4IvPERMBZAPH6cmMutHNWVtIdPyiTi93gCn8oz3D0UX8QT3Yc9Qhx0KLw7AzLajAXSc7zdOebtovZmOejyl4YrYO4Q2rZ3ODTqotFgXGJWNFkg/PoV+Yzy4tILOujDlTHUv3/BkFaj4TSnzgl7NzstdZpNM1R0KBkHlDuM10K9yyLEmNlHSvx8zTZWBTEkzb1FoU=
I've tried looping through SSH.NET's Client.ConnectionInfo.HostKeyAlgorithms
dictionary, but I'm not sure where to get the byte array that's required by an entry's value invocation:
Dim aData As Byte() = Nothing
For Each oAlgorithm In oClient.ConnectionInfo.HostKeyAlgorithms
Dim oInstance = oAlgorithm.Value(aData)
End For
That code fails, as the Func(Of Byte(), KeyHostAlgorithm)
invocation requires an actual array with elements. It also rejects an empty array (and any other array, for that matter). For example:
oAlgorithm.Value({})
The requested length (4) is greater than the actual number of bytes read (0).Parameter name: length
oAlgorithm.Value({0, 1, 2})
The requested length (4) is greater than the actual number of bytes read (3).Parameter name: length
oAlgorithm.Value({0, 1, 2, 3})
Data longer than 2147483647 is not supported.
What gives here? What's that byte array parameter supposed to be and where does one get it?
I also tried this in the Client.HostKeyReceived
event handler:
Dim sHostKey = Convert.ToBase64String(New SHA256Managed().ComputeHash(e.HostKey))
But that calculated Base64 string differs from the entry currently in my known_hosts
file for that host (see above).
How do I retrieve the various keys from the SSH host, in a way that matches what's in known_hosts
(ed25519
and rsa
in particular)?
--EDIT--
I've succeeded in discovering the byte array to be used in the function invocation:
oHandler = Sub(Sender As SshClient, e As HostKeyEventArgs)
Dim aData As Byte()
For Each oAlgorithm In Sender.ConnectionInfo.HostKeyAlgorithms
Dim oInstance = oAlgorithm.Value(e.HostKey)
If TypeOf oInstance.Key Is Ed25519Key Then
aData = DirectCast(oInstance.Key, Ed25519Key).PublicKey
Exit For
End If
Next
End Sub
However, the computed Base64 of aData
still doesn't match the output of the Win11 SSH client. Does anyone know how those values are arrived at?
--EDIT--
OK, I'm getting closer. I now have the Windows-supplied SSH client's value for ed25519
. It's a straight Base64 encoding of the server's HostKey
, prior to any hashing:
Dim sBase64 = Convert.ToBase64String(e.HostKey)
' Returns AAAAC3NzaC1lZDI1NTE5AAAAICuy9wsbY7fBFFoB6K0RU/BsISB3mL4PQ3311ta0S/cW
Now to tackle getting the same for the server's rsa
key.
The solution is much simpler than I had anticipated: simply Base64-encode the host's HostKey
byte array without hashing it (SHA256
or MD5
).
I was able to get the rsa
key by requesting it at connection time:
sKeyType = "ssh-rsa"
oConnectionInfo = New PasswordConnectionInfo(Host.Name, Host.Username, Host.Password)
oConnectionInfo.HostKeyAlgorithms.Clear
oConnectionInfo.HostKeyAlgorithms.Add(sKeyType, Function(Data) New KeyHostAlgorithm(sKeyType, New RsaKey, Data))
Using oClient = New SshClient(oConnectionInfo)
AddHandler oClient.HostKeyReceived, oHandler
oClient.Connect
oClient.CreateCommand("temp").Execute
RemoveHandler oClient.HostKeyReceived, oHandler
End Using
oHandler = Sub(Sender As SshClient, e As HostKeyEventArgs)
sBase64 = Convert.ToBase64String(e.HostKey)
e.CanTrust = Host.Base64 = sBase64
End Sub
This works, and I can use it to compare the host's keys with the Windows-supplied SSH client's stored keys.