Creating SSH Authentication Keys

To use public key authentication with SSH or SFTP, you need to generate a public and private key pair. In some cases, a third-party may generate these keys for you as part of access to their service. Alternatively, you may be required to generate the key pair yourself, and this article will explain the process and describe how to generate a key pair for use with SSH on a Windows 10 or Windows 11 system.

Overview

SSH and SFTP support the use of cryptographic keys for authentication in addition to, or in place of, the traditional password. The primary advantage to using public key authentication is the account password is never transmitted over the network, reducing the risk of the user's credentials being intercepted. It can also be more convenient, because once it has been configured, the user doesn't need to entire their password and their password does not need to be stored by the client application. The overall process generally works like this:

  1. You generate a pair of cryptographic keys. One is a public key that can be shared with anyone, and the other is a private key which should be kept secret and only accessible to you. In practical terms, the private key should be protected in the same way that the user's password is protected; typically, it is stored as a file (or in a database) with access limited to the user.
  2. You place the public key on the server you want to access. For most SSH servers this will be a file named .ssh/authorized_keys which is located in the user's home directory. This same convention is used for both UNIX-based systems and OpenSSH for Windows. This is a simple text file which can contain multiple public keys, with each key stored on its own line.
  3.  When you connect to the server using public key authentication, the server will check to see if you have a public key in your authorized_keys file. It will use that public key to encrypt some data (called the "challenge") and will send that data back to the client. If the client can decrypt the challenge data using your private key, it is sent back to the server, authenticating the client session. Because only your private key is capable of decrypting the challenge data, it proves you are in possession of the private key without having to exchange any sensitive data with the server.

Key Generation

On Windows 10 and later systems, there is a tool called ssh-keygen which you can use to generate a public and private key pair. You can do this from a terminal window with a command like this:

ssh-keygen -t rsa -b 4096

The ssh-keygen utility has a large number of options, but this command generates a key with 4096 bits which is sufficiently secure for most users. You will see a message that the utility is generating a public/private key pair, and then will prompt you for a file name. By default, it will create the private key file in a folder named .ssh under you home directory (e.g.: C:\Users\MyName) and will be named id_rsa. It will also create the public key file in the same location, with a file extension of .pub. For example, if you accept the defaults, two files would be created in the .ssh folder, one named id_rsa (the private key) and one named id_rsa.pub (the public key).

Both keys are in a text format and can be viewed in any text editor. The private key is stored as a PEM encoded format which is essentially a base64 encoded block of data with markers that indicate the beginning and end of the key. It will look something like this:

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAgEArabF4or77SIXMeZIYsqerYfvruDA3IGLzd7N2Ua2o4lbVJI/xifq
ZygMZC0DxTlB0KX1bB7IrZ2f7wij+aqZn53DU03vbljwjsQ1vyRpWtyq98mDLSL5Fh5xHy
2gSGOwkp4V42qvxxtr5oyduNZZHH30IefslpkjExQEQy1mF10uhfN2vYvwYFEarJLkZphV
yNPd1tdmDt/VVppW/XxhXiP9Kb3mJWS/4Vn1fO03VPaOH0EZBoltI+SyA9ovDknSAnoY3n
bujxIKPetS4i2ueGp0F3kN7dbhowx/Sv2L6fSmf2M8DFlxysOepcTn5kjnuBq9mG2z1wb3
fRXWVm+xy4XHRwC3AC0gco4JsBO+B+SVfJ5oc10eBXRpT1IAVpgs+MwiP59JVVbFgHjQaL
9S/nhBO0/fhtGwVsgW9zhvDNqwHgWsEN+ahCanNt3Xz+4+486b6c6Sd++S0hTZoDUmiBNC
WzYWJZAomi2ORyavgYn8g5M/Fp9q4pCbey5K25M2a2VtGGwk9fyJLF+yDMw++tdY4qnjZ5
l29qzZR72Z2EQ9B6F4ryJO4ellOK8oCjrjijj8SEnLyR6QnNSVWd0m3U4Q1695jjl78BD1
4Kgp6l4Et6l5ZR2bk9DxGXbrAeW958O3n21Ghw0i7LPTzDaqDL9BTZk1PT0tOOAbXhsBg0
kAAAdAVZqHwFWah8AAAAAHc3NoLXJzYQAAAgEArabF4or77SIXMeZIYsqerYfvruDA3IGL
zd7N2Ua2o4lbVJI/xifqZygMZC0DxTlB0KX1bB7IrZ2f7wij+aqZn53DU03vbljwjsQ1vy
RpWtyq98mDLSL5Fh5xHy2gSGOwkp4V42qvxxtr5oyduNZZHH30IefslpkjExQEQy1mF10u
hfN2vYvwYFEarJLkZphVyNPd1tdmDt/VVppW/XxhXiP9Kb3mJWS/4Vn1fO03VPaOH0EZBo
k41G61Sd7k9CM1EC2wH6noKzdZ5Lg/XgitXqw1iEz0/di0DzOMsNOV4gIohr71oeSPF57l
kS7Yr4Oebp0AAAALZm9vQGJhci5jb20=
-----END OPENSSH PRIVATE KEY-----

The public key is also a text file which only has a single line of text, identifying the algorithm used to create the key (RSA in this case) and a base64 encoded version of the data. This line of text is what should be appended to the authorized_keys file on the server. There are a few different methods which can be used to let the SSH server know to use the public key you've generated. If you already have access to the server using SSH or SFTP using a password, you can create or append the public key to the authorized_keys file yourself.

If you don't have the ability to add the public key yourself, you should contact the server administrator and they should be able to add the public key to your account.

Public Key Authentication with SocketTools

To use your private key for authentication with your applications, you need to tell the SocketTools component or library where to find the private key. If you are using the .NET or ActiveX Editions, this is done by simply setting the value of the PrivateKey property. For example, this is a simple console application written in C# using the SocketTools.FtpClient class to list the files in a user's home directory:

using SocketTools;
FtpClient ftpClient = new FtpClient();

if (!ftpClient.Initialize())
{
    Console.WriteLine("Unable to initialize the SocketTools component");
    return 0;
}

ftpClient.HostName = "my.server.tld";
ftpClient.RemotePort = 22;
ftpClient.UserName = "myusername";
ftpClient.PrivateKey = @"%USERPROFILE%\.ssh\id_rsa";
ftpClient.Options = FtpClient.FtpOptions.optionSecureShell;

// Connect to the server and authenticate using a private key
if (!ftpClient.Connect())
{
    Console.WriteLine("Unable to connect to {0}", ftpClient.HostName);
    return 0;
}

// Open the current user's home directory and begin listing each of
// the files and directories
if (ftpClient.OpenDirectory())
{
    FtpClient.FileInformation fileInfo = new FtpClient.FileInformation();
    bool bResult;

    bResult = ftpClient.GetFirstFile(ref fileInfo);
    while (bResult)
    {
        Console.WriteLine("File={0} Size={1}", fileInfo.FileName, fileInfo.FileSize);
        bResult = ftpClient.GetNextFile(ref fileInfo);
    }

    ftpClient.CloseDirectory();
}

// Disconnect from the server after we have finished
ftpClient.Disconnect();
return 0;

When using the Library Edition APIs, the process is a bit more complex because it requires creating a SECURITYCREDENTIALS structure which would then be passed to the FtpConnect function. Here's an example of how that could look:

LPSECURITYCREDENTIALS lpSecCred = NULL;
LPCTSTR pszPrivateKey = _T("%USERPROFILE%\\.ssh\\id_rsa");

bResult = FtpCreateSecurityCredentials(
    SECURITY_PROTOCOL_SSH2, // The security protocol used with these credentials
    0,                      // Additional options; this should be 0 for SFTP
    NULL,                   // Username; this is not required to access the private key
    NULL,                   // Password; this is for the private key, not the user account
    pszPrivateKey,          // Full path to the private key file
    NULL,                   // Certificate name; unused for SFTP
    NULL,                   // Reserved; this should always be NULL
    &lpSecCred              // A pointer to SECURITYCREDENTIALS on return
);

As you can see, most of the parameters to the FtpCreateSecurityCredentials function are NULL, with the only required parameters being the protocol type, the path to the private key file and a pointer to the structure which will be passed to FtpConnect. That function call would look something like this:

LPCTSTR pszHostName = _T("my.server.tld");
LPCTSTR pszUserName = _T("myusername");

HCLIENT hClient = FtpConnect(
    pszHostName,             // Server host name or IP address
    FTP_PORT_SSH,            // Server port number, default for SSH is port 22
    pszUserName,             // Username; this cannot be NULL or an empty string
    NULL,                    // Password; this parameter is not used with private keys
    FTP_TIMEOUT,             // Timeout period
    FTP_OPTION_SECURE_SHELL, // Options
    lpSecCred                // The security credentials defined above
    );

One important thing to note, regardless of whether you're using the .NET class, the ActiveX control or the API, is that you do not need to provide the user's account password. If you are using the Library Edition, we have a separate article which describes in more detail how to use our API with C/C++ to define the security credentials, with an example which is similar to the one shown above for C#.

See Also

SFTP Public Key Authentication in C/C++
Creating a Certificate Using OpenSSL

Shopping Cart
Scroll to Top