SFTP Public Key Authentication

There are two primary authentication methods supported with SFTP (SSH) which you can use with your applications. The first is password authentication, when you provide a username and a password, and the client session is authenticated just as it would be with a standard FTP session. The second method is to use public key authentication, in which you generate a public and private key pair; the public key is stored on the server, and the private key is used (along with the username) to authenticate the client session.

Public and Private Key Files

To use public key authentication with the SocketTools FTP client API, you will need to specify the path to the file which contains the private key. The private key file should be in a standard text PEM encoded format which looks something like this:

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAgEAtalUJr6lXcw0K/dDuPJqgoVErWhWT4K4OseEMbAGS45Kn/1pZfDj
2fvuHs1yhjYHcnxBdXOak4Ik6ejYNf36dckXRfJF/cpmutJpNKAJ1Cz5A0FAdgGcJJ070B
S5hYkEnAyyS4pKiVQWJRCE5HmpNGfp2wb3ZCuwrQdAyWyGFdiy50pqNKfLuyQ0sjhiT0Ql
nWI2wIvdjErJQWgtfPN37z/rKLiDjoFnMe52smnXYvrW16AU3ubXDRPzHV09VxMbJ4iWcD
ZNcgQQOe0kUUdI5kBKkwk80fm1VsczIPCLq3dzDR2f3BU3AuRUDa3NjJkx1qz9YSUUaHtp
PKyJhaV01NZxYBPX/YTK48Cmkztt+rGTpd3mALepXlNS3bEHhhQJJWvkmTsiRxQVq7gJTn
...
-----END OPENSSH PRIVATE KEY-----

If you have generated the private key using the ssh-keygen utility which is included with Windows 10 and 11, by default it will create the key pair in a subfolder named .ssh in your home directory and the default file names are typically id_rsa for the private key and id_rsa.pub for the public key. This is largely for compatibility with UNIX-based systems, but you can actually store the key pair in any location and choose any file name you prefer. The most important thing to keep in mind is the private key file should not be stored in a publicly accessible folder.

Security Credentials

Using a private key file to authenticate an SFTP session, you need to create a SECURITYCREDENTIALS structure and provide a pointer to that structure to the FtpConnect function. The simplest way to do this is using the FtpCreateSecurityCredentials function which allocates a new structure and returns a pointer to that structure for use in your application. Here's how it can be used with an SFTP client session:

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
);

Most of the parameters passed to the function will be NULL because they aren't required for the SFTP connection. If the private key was created with a password, that can be provided here as well; however, keep in mind that it is not the user password for the account on the server. It should only be specified here if the private key was created using a password (e.g.: using ssh-keygen).

It is recommended that you always provide an absolute (full) path to the private key, rather than a relative path. This eliminates any ambiguity and ensures the correct private key is being loaded. Our API allows you to include embedded environment variables in the file name by surrounding them with % symbols. In the example shown above, you can see that it uses the USERPROFILE environment variable, which is defined as the current user's home directory on the Windows system.

Connecting to the Server

After you have defined the SECURITYCREDENTIALS structure, you need to provide it to the FtpConnect function. If you're familiar with using the SocketTools FTP API, in most cases you've passed a NULL for this argument and provided the typical username and password string parameters for authentication. To use public key authentication, the function call will change to 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
    );

Take note of a few things in this example code. The hostname and username are the same as they would be with a connection using typical password authentication; however, the password argument is not defined. If you are authenticating with a private key, this should always be NULL or a zero-length string. If the private key was created using a password, that should be specified when the SECURITYCREDENTIALS structure is created and not provided as the password argument to the FtpConnect function.

We also recommend that you always use the FTP_OPTION_SECURE_SHELL option when you're using SFTP. This is particularly important when you're not using the default SSH port (22); if you're connecting on a non-standard port, by default the FTP API will presume you're using FTP or FTPS and the connection attempt will fail. By always including FTP_OPTION_SECURE_SHELL, you're ensuring the API knows that you only want to use SFTP for the connection and the credentials provided only apply to an SFTP session.

Example Code

The following example demonstrates how to use a private key for authentication with an SFTP server using the SocketTools FTP API. After connecting to the server, it will list all of the regular files and their size in the user's home directory.

#include <cstools11.h>
#include <stdio.h>

int main()
{
    HCLIENT hClient = INVALID_CLIENT;
    LPCTSTR pszHostName = _T("my.server.tld");
    LPCTSTR pszUserName = _T("myusername");
    LPCTSTR pszPrivateKey = _T("%USERPROFILE%\\.ssh\\id_rsa");
    LPSECURITYCREDENTIALS lpSecCred = NULL;
    BOOL bResult;

    // Initialize SocketTools, which loads the network and security
    // libraries. This must be the first function called by your
    // application.

    bResult = FtpInitialize(CSTOOLS11_LICENSE_KEY, NULL);

    if (!bResult)
    {
        _tprintf(_T("Unable to initialize SocketTools library\n"));
        return 0;
    }

    // Define the security credentials used to authenticate the user.
    // This function can be used for both SFTP (SSH) private keys and
    // FTPS (TLS) client certificates. In this case we are using a
    // private key for an SFTP session.
    //
    // When this function returns, the lpSecCred variable will point
    // to a SECURITYCREDENTIALS structure which defines the credentials
    // used with the subsequent call to the FtpConnect function.
    // 
    // Note the private key file name can include embedded environment
    // variables which are automatically expanded

    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
        );
    
    if (!bResult)
    {
        _tprintf(_T("Unable to create security credentials\n"));
        return 0;
    }

    // Establish a connection with the SFTP server using the specified
    // hostname (or IP address) and username. 

    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
        );

    if (hClient == INVALID_CLIENT)
    {
        _tprintf(_T("Unable to connect to %s\n"), pszHostName);
        return 0;
    }

    // The security credentials are no longer needed after the connection
    // has been established; this releases the memory allocated by the
    // FtpCreateSecurityCredentials function and resets lpSecCred to NULL

    FtpDeleteSecurityCredentials(&lpSecCred);

    // Open the current working directory for the user and begin listing
    // each of the regular files in the directory

    if (FtpOpenDirectory(hClient, NULL) != FTP_ERROR)
    {
        FTPFILESTATUSEX ftpFile;

        bResult = FtpGetFirstFileEx(hClient, &ftpFile);
        while (bResult)
        {
            if (!ftpFile.bIsDirectory)
            {
                // If this is not a directory, print the file name and size
                _tprintf(_T("%-20s %I64u bytes\n"), ftpFile.szFileName, ftpFile.uiFileSize.QuadPart);
            }

            bResult = FtpGetNextFileEx(hClient, &ftpFile);
        }

        // Make sure to close the directory when we are finished
        FtpCloseDirectory(hClient);
    }
    else
    {
        // The FtpOpenDirectory function failed on the user's home directory
        // and this typically indicates some permissions related problem.
        // The FtpGetLastError function can return more specific information
        // about the cause of the failure.

        _tprintf(_T("Unable to open the current working directory\n"));
    }

    // Disconnect from the server and release the memory allocated for
    // this client session

    FtpDisconnect(hClient);
    return 0;
}

See Also

Creating SSH Authentication Keys
Creating a Certificate Using OpenSSL

Shopping Cart
Scroll to Top