Multi-Factor Authentication with Password and Private Key File in SSH.NET

If you want to use SSH.NET to connect to an SFTP server that requires both a password and a private key file, you’ll be happy to know that it is well supported! Unfortunately (and similar to my previous SSH.NET post about modifying the Host Key Algorithm) the documentation doesn’t really make it clear how to do this.

As an example, let’s grab the code sample from the SSH.NET documentation on multi-factor authentication. At time of writing it looks as follows:

var connectionInfo = new ConnectionInfo("sftp.foo.com",
    "guest",
    new PasswordAuthenticationMethod("guest", "pwd"),
    new PrivateKeyAuthenticationMethod("rsa.key"));

using (var client = new SftpClient(connectionInfo))
{
    client.Connect();
}

If you modify the server address, username and password and then run it you’ll quickly hit a brick wall in the shape of this error message:

Renci.SshNet.Common.SshAuthenticationException: Permission denied (publickey).

The first issue that we’re running into is that we’re not actually specifying the private key details anywhere! Let’s change that (I’ve also swapped the literal strings out for some variables):

string _privateKeyPath = "";
string _privateKeyPassPhrase = "";
string _host = "";
string _username = "";
string _password = "";

var keyFile = new PrivateKeyFile(_privateKeyPath, _privateKeyPassPhrase);
var keyFiles = new[] { keyFile };

var connectionInfo = new ConnectionInfo(_host, _username,
    new PasswordAuthenticationMethod(_username, _password),
    new PrivateKeyAuthenticationMethod("rsa.key", keyFiles));

using (var client = new SftpClient(connectionInfo))
{
	client.Connect();
}

Now if you run this you’ll get a new error (#ProgressIsProgress), this time telling you that you’re not allowed to change username:

Renci.SshNet.Common.SshConnectionException: The connection was closed by the server: Change of username or service not allowed: (<USERNAME>,ssh-connection) -> (rsa.key,ssh-connection) (ProtocolError).

Now when I first ran into this error and threw it into Google I got very few results; looks like we’re treading new ground!

The fix for this is actually really simple; the constructor for PrivateKeyAuthenticationMethod takes a username as the first parameter! For some reason this is set to “rsa.key” in the official documentation rather than “guest” which they use as the username placeholder on the two lines above, so when I first looked into this issue I wrongfully assumed that it was a string specifying what type of private key was being consumed!

The only change we need to make to the previous example is to swap in the _username variable (or a literal string of the username if you prefer) for the first parameter of the PrivateKeyAuthenticationMethod constructor.

string _privateKeyPath = "";
string _privateKeyPassPhrase = "";
string _host = "";
string _username = "";
string _password = "";

var keyFile = new PrivateKeyFile(_privateKeyPath, _privateKeyPassPhrase);
var keyFiles = new[] { keyFile };

var connectionInfo = new ConnectionInfo(_host,
    _username,
    new PasswordAuthenticationMethod(_username, _password),
    new PrivateKeyAuthenticationMethod(_username, keyFiles));

using (var client = new SftpClient(connectionInfo))
{
	client.Connect();
}

Run it now, and you should connect successfully!

One thought on “Multi-Factor Authentication with Password and Private Key File in SSH.NET

Leave a Reply to Andile Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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