Changing Host Key Algorithm in SSH.NET

I’ve used SSH.NET a lot over the years to send and receive files using SFTP and it’s a very flexible and practical library, but the documentation can be a bit thin on the ground when you’re looking to use some of the more esoteric features it has.

As an example, I recently ran into an issue where I was connecting to a remote server and the host fingerprint I was receiving through SSH.NET didn’t match the one that I expected to see (and could see in WinSCP). After verifying that I was using the same connection settings on both and more than a little spelunking through the SSH.NET source code I found that by default the host key algorithms used by the stable release of SSH.NET that I was on (2016.1.0) are RSA and DSA, while WinSCP uses Ed25519. For my purposes I needed to use Ed25519 in SSH.NET as well even though the SFTP host also supported these other algorithms.

Supported Host Key Algorithms

According to the GitHub repository README, SSH.NET supports the following host key algorithms:

  • ssh-ed25519
  • ecdsa-sha2-nistp256
  • ecdsa-sha2-nistp384
  • ecdsa-sha2-nistp521
  • ssh-rsa
  • ssh-dss

Now this is only accurate if you’re using 2020.0.0-beta1, the stable 2020.0.0 release or later. As such, if you’re wanting to use any of the algorithms on the list other than RSA or DSS, you’ll need to install the latest version.

Configuring Available Algorithms and Order

So now we’ve got a set of algorithms available to us. How can we configure which ones will be used, and in what order? The answer lies in the HostKeyAlgorithms dictionary on ConnectionInfo.

The client will negotiate with the server to determine which algorithm will be used by going through the dictionary in the order that the algorithms were added* and trying each until one is found that is mutually supported (by default the algorithms are added in the order listed above, with ssh-ed25519 first and ssh-dss last). As such, adding or removing items from the dictionary or modifying the order they’re in* will change the order that the client attempts to use them.

*See the section “The Pitfalls of Dictionary Ordering” for a discussion around the potential issues with relying on the order items are added to a dictionary.

Example

So let’s see this in action. Below is a chunk of code that creates a ConnectionInfo object and then uses it to attempt to connect to the target server. A delegate prints the host key when it is received during the initial connection.

var _username = "example";
var _password = "test";
var _host = "localhost";
var _port = 22;

var methods = new List<AuthenticationMethod>
{
	new PasswordAuthenticationMethod(_username, _password),
};

var connectionInfo = new ConnectionInfo(_host, _port, _username, methods.ToArray());

//connectionInfo.HostKeyAlgorithms.Clear();
//connectionInfo.HostKeyAlgorithms.Add("ssh-rsa", (data) => { return new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data); });

Console.WriteLine("Host key algorithms:");
foreach (var hostKeyAlgorithm in connectionInfo.HostKeyAlgorithms)
{
	Console.WriteLine($"- {hostKeyAlgorithm.Key}");
}

using var sftpClient = new SftpClient(connectionInfo);
sftpClient.HostKeyReceived += delegate (object sender, HostKeyEventArgs e)
{
	//`e.FingerPrint` will contain the fingerprint returned by the host
	Console.WriteLine(BitConverter.ToString(e.FingerPrint));
};

sftpClient.Connect();

Running the above against a locally hosted SFTP server produces the following (the username and password don’t matter since the host key will be returned as part of the initial connection before credentials are exchanged):

Host key algorithms:
- ssh-ed25519
- ecdsa-sha2-nistp256
- ecdsa-sha2-nistp384
- ecdsa-sha2-nistp521
- ssh-rsa
- ssh-dss
F2-97-35-7C-5E-35-E5-D7-48-B7-B9-51-E3-D6-B2-A4

Uncommenting lines 13 and 14 allows the section that clears the list and adds only ssh-rsa to run. Running the code now produces the following input instead:

Host key algorithms:
- ssh-rsa
9A-8C-76-E9-12-58-2B-F9-4F-BB-C3-EA-6B-66-A6-3D

You can use any of the normal methods of modifying an IDictionary, although it’s worth mentioning that HostKeyAlgorithms is readonly so cannot be directly assigned to (this doesn’t prevent adding or removing items from the dictionary though).

The exact sections of code needed to specifically add any of the algorithms that SSH.NET supports are as follows (borrowed from the implementation that sets up the HostKeyAlgorithms on ConnectionInfo in the first place):

//ssh-ed25519
connectionInfo.HostKeyAlgorithms.Add("ssh-ed25519", (data) => {return new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(), data); });
//ecdsa-sha2-nistp256
connectionInfo.HostKeyAlgorithms.Add("ecdsa-sha2-nistp256", (data) => {return new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data); });
//ecdsa-sha2-nistp384
connectionInfo.HostKeyAlgorithms.Add("ecdsa-sha2-nistp384", (data) => {return new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data); });
//ecdsa-sha2-nistp521
connectionInfo.HostKeyAlgorithms.Add("ecdsa-sha2-nistp521", (data) => {return new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data); });
//ssh-rsa
connectionInfo.HostKeyAlgorithms.Add("ssh-rsa", (data) => {return new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data); });
//ssh-dss
connectionInfo.HostKeyAlgorithms.Add("ssh-dss", (data) => {return new KeyHostAlgorithm("ssh-dss", new DsaKey(), data); });

The Pitfalls of Dictionary Ordering

If you read the above and recoiled when I mentioned about relying on the inherent ordering of a dictionary I wouldn’t blame you. According to the Microsoft documentation on Dictionarythe order in which the items are returned is undefined“, so expecting to receive items back in the order they were added is not guaranteed and could change based on platform or implementation.

I’ve raised an issue on the SSH.NET GitHub project, but it may take some time before there’s a resolution (if it’s deemed a necessary fix that is). Until then, since SSH.NET is already relying on dictionaries in this way the above approach is the only effective way to modify the host key algorithms in use.

One thought on “Changing Host Key Algorithm in SSH.NET

  1. Hi Matthew,

    kudos for your post! It’s well written, detailed and it helped me in understanding SFTP/SSH a bit more.

    I am struggling myself with using the correct host key algorithm, although with a different framework (WeOnlyDo), that’s how I found your post.

    Keep up the great work.

    Kind regards,

    Joeri

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.