SSH Keys, Part 2: Client Verification

So far in this series of posts on ssh on macOS:

Please consider supporting Scripting OS X by buying one of my books!

In the previous post we discussed how ssh uses public and private keys to secure the data sent back and forth, between the remote computer (host) and your machine (client). The keys allow you to be sure that you are talking to the correct host and not an imposter.

However, how does the host know that you are who you claim to be?

Open Sesame…

The default way to authenticate yourself to the ssh host is to give it the correct username and password combination. This is and has been a common practice for computers, servers, websites for a long time and it works for ssh as well.

You will obviously need an account on the host for this to work. In larger organizations the computers will often be connected or bound to a directory service, to centralize the user and group management. If the computers are not bound to a central directory, as is often the case with mobile computers, you can use your management system to install one or more managed users on the system.

In the Sharing Preference Pane, you can control which users or groups of users can use ssh or ‘Remote Login’ to connect to this Mac. Behind the scenes the users and groups that can connect with ssh are in the group. You can find some scripts to manage the controls in this post.

Keys for the Client

Passwords, and user access control groups already provide a strong security, but have some disadvantages as well:

  • you have to re-enter the password on every connection. This is especially tedious when you send individual commands with the ssh hostname command syntax
  • automation is insecure because you have to provide the password in the script (in clear text)

With ssh we can use the same asymmetric keys to validate the client to the host, as we did to verify the host.

Sidenote: you will see a lot of actual keys and fingerprints in this post. While sharing the public key should not be a security issue, I deleted them all after finishing this post, just to be safe.

Creating a Key

First, you need to generate the key for your user. This is simple enough with ssh-keygen:

$ ssh-keygen -t rsa -b 2048 -C "Armin RSA 2017-07-06"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/armin/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /Users/armin/.ssh/id_rsa.
Your public key has been saved in /Users/armin/.ssh/
The key fingerprint is:
SHA256:jRydlFk3rRTpuKshFvnzon7UnzcYiHLQhKpQvLqPrl0 Armin RSA 2017-07-06
The key's randomart image is:
+---[RSA 2048]----+
|  .     . .+. ++ |
|   o   . +o. .o..|
|  . . . + o  + . |
| . . . o *  . o  |
|  o .   S o...   |
| . .   . =..o.   |
|  . E   =.+  oo. |
| o..   . .o+..o..|
|+oo.   .oo.o.  ..|

This command will create a new key pair of type ‘rsa’ (-t rsa) with a bit length of 2048 (-b 2048) and a comment of Armin RSA 2017-07-06.

RSA is the recommended type for best cross platform compatibility. Mac hosts and clients also support ECDSA, and ED25519. (DSA is in the list but considered insecure and obsolete.) When you connect to other, non-macOS, hosts, ask the administrators for the best key type to use.

The bit length of the key determines how complex the key is. Longer bit lengths are more secure, but require more CPU power for the encryption and decryption. Different key types can have different values for the bit length, read the ssh-keygen man page for details.

The comment is optional, but provides a way to add a label to a key which will help you identify it later. I usually add who created and when and if the key is for a specific purpose.

Next the ssh-keygen tool will ask where to store the key pair. Since this will be our ‘generic’ key, the default name and location ~/.ssh/id_rsa (or id_ecdsa, etc.) is exactly what we want. Sometimes you may have to create a key specifically for a single host, then you can choose a different name here, so you do not overwrite your other keys.

Next the tool will ask you for a passphrase. This will be used to encrypt (lock) the private key, so even if someone gets their hands on the private key file, they will not be able to use it. Since the private key represents your ssh identity it should be protected well. Use a password generator to create a long passphrase. Store the passphrase in a secure place (Password manager, you will need it again) and enter it here (twice).

Finally, the tool will show the files it generated (the private and public key), its fingerprint (with the comment) and a ascii art representation of the key, which is supposed to be more visually recognizable than the alphanumeric fingerprint.

Copying the Key to the Host

When we set up an ssh connection to a host we need to copy its public key into our ssh settings (the known_hosts file). Similarly we need to copy our public key to the host.

You have to copy the public key (i.e. to the host and append its contents to the ~/.ssh/authorized_keys on the host in your user home directory. On some systems, the file may also be called authorized_keys2 and/or be in a different location.

On Mac OS X versions 10.11 and older you can use this command:

$ cat ~/.ssh/ | ssh [user@] "cat >> ~/.ssh/authorized_keys"

This will pipe the contents of your public key file into ssh, which sends it to the remote host and appends (>>) it to the ~/.ssh/authorized_keys file. (This will fail if the ~/.ssh directory does not exist yet on the host.)

On many Linux systems and on macOS Sierra 10.12 there is a tool to help with this. You can use

$ ssh-copy-id [user@]

By default, the ssh-copy-id command will copy all available public keys to the host. You can tell it to use a specific key with the -i option. Either way, since the key is not yet in place, the command will need to provide you with the host’s password to authenticate to the host.

The ssh-copy-id command gives helpful additional information:

$ ssh-copy-id
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/Users/armin/.ssh/"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys

Number of key(s) added:        1

Now try logging into the machine, with:   "ssh ''"
and check to make sure that only the key(s) you wanted were added.

Now, when you attempt to connect to the remote host, you will be prompted to unlock your private key:

$ ssh
Enter passphrase for key '/Users/armin/.ssh/id_ecdsa': 
Last login: Thu Jul  6 09:00:28 2017 from [IP Address]
mac:~ armin$

and when you look at the contents of ~/.ssh/authorized_keys you will see your public key listed:

mac:~ armin$ more .ssh/authorized_keys 
ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAC3TR7ZYSjJO01JVNYMeXWNNgglNulQVuGGTnE2JiBULbs9In9uWrFWyqOXodi4XCs7lY92undONnN4wQKumsN/RgBnEDATKZZdh5OPI72ZGrmnPNXVUbU8mjzIJ4LpWfTu0x6nqR9zBu1LPNW9xPoQNwTogh7jIHxvEcYav1slKjeRkg== Armin ECDSA Key 2017-07-06

Note that the comment we added when creating the key is part of the public key (and its fingerprint). Considering that this file can contain multiple keys from different sources, the comment will help a lot in locating the proper key.

When a key is compromised or just not used any more, it should be removed from the host’s authorized_keys file. This file should be audited frequently.

Agent of Keys

When we connected to the remote host after copying the key, we did not have to enter the password for the host, because the key pair confirmed our identity. However, you still had to unlock the private key with the passphrase. You stored that somewhere secure, right? Without the passphrase the key is useless.

This does simplify passwords, because you can use the same key for multiple hosts, and you don’t have to memorize all those passwords. However, entering the passphrase over and over is tedious and error prone, too. Thankfully there is a solution to this. There is a process that can store the passphrase for the duration of your login session. The process is called ssh-agent. However you do not talk directly with this tool but use ssh-add to manage it instead. And on macOS you don’t even have to do that, because ssh-add and ssh-agent are integrated with the macOS Keychain.

The role of the ssh-agent is to hold on to your private key passphrases for the duration of your login session. On other systems (or with Keychain integration disabled) you have to add the passphrase once per login session with

$ ssh-add ~/.ssh/id_rsa

(this will prompt for the passphrase)

You can list the passphrases that ssh-agent is holding with

$ ssh-add -l

and clear all of them with

 $ ssh-add -D

Keychain Integration

Note: on macOS Sierra (10.12), Keychain integration is not enabled by default. To enable it, add these two lines at the top of ~/.ssh/config (create the file if necessary):

AddKeysToAgent yes
UseKeychain yes

With Keychain integration enabled, ssh-agent will also look for passphrases stored in the keychain and then remember them for the rest of the session. The passphrase is stored as an application password with the path to the private key in the name. So as long as the keychain is unlocked, it will find the passphrase in the keychain and use it to unlock the private key (and remember it for the rest of the login session).

On macOS Sierra, the passphrases are not stored in standard login keychain and not synced with iCloud.

Managing Keys with multiple clients

If you have many client Macs you use to connect to the same hosts, you have two choices:

  • you can create a new key pair in each client and add the public key to the host’s authorized_keys file
  • you can copy the key pair from one client to another

The first option can grow unwieldly quickly when you are managing several Macs and several users.

The second option might expose your private key when you copy them from one client to another. If you send the private key unencrypted over the network it can be intercepted by a man-in-the-middle attack. If you copy it on a USB stick to transfer, you should remember to securely delete any copies, after the transfer.

Your organization will (should) have rules on how to setup and manage keys.


Given proper key management, client verification with keys is safer than with username and passwords. Because of this some setups only allow ssh connections an authorized key.

In this case you cannot use ssh-copy-id to transfer and add your public key to the host. You will have to provide the public key to an administrator to add it for you. The authorized_keys file will probably be in a different, central and locked-down location.

Automation with ssh and keys

You can write scripts that use ssh to communicate with another host. This can yield some powerful workflows. However, if the script uses ssh repeatedly, you do not want to have to enter the password over and over. If the script runs within a user session, ssh-agent can provide the passphrase when necessary. But when a script has to run in background on a schedule without user interaction, ssh-agent will not be available.

In this case you can create a key with an empty passphrase. This key will be unlocked automatically. Obviously, such a key would need to be protected very well, and its use should be audited closely. A key with an empty passphrase is the security equivalent of leaving the house key under the doormat. However, for some automated workflows, it may be the best solution.


  • previous post: ssh uses public/private keys to verify the host and encrypt the connection
  • ssh can also use public/private keys to verify the user connecting to the host
  • you generate a key with ssh-keygen
  • the public key is added to the remote host’s ~/.ssh/authorized_keys
  • the private key remains on the client and is locked with a passphrase
  • ssh-agent and Keychain integration on macOS can simplify access to the private key’s passphrase

Next post: Transferring Files with ssh

Leave a Reply

Your email address will not be published. Required fields are marked *