Protecting SSH keys

Public/private Keys are a great way to handle authentication when working with SSH, however public-key cryptography relies on you keeping your private key private. There are a few ways you can achieve this when working with OpenSSH.

File permissions

Private key files should never be world readable, the parent directory should also be restricted to prevent anyone other than the owner accessing the files in the directory:

$ ls -ld ~/.ssh/ ~/.ssh/id_rsa
drwx------. 2 bob bob   38 Jun  6 22:27 /home/bob/.ssh/
-rw-------. 1 bob bob 1679 Jun  6 22:27 /home/bob/.ssh/id_rsa

If for any reason your private key has read access for group or other, you will probably get a message similar to the following when trying to use the key:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0664 for '/home/bob/.ssh/id_rsa' are too open.

File permissions can be restricted with chmod:

$ chmod -v 0700 ~/.ssh/ ; chmod -v 0600 ~/.ssh/id_rsa
mode of '/home/bob/.ssh/' retained as 0700 (rwx------)
mode of '/home/bob/.ssh/id_rsa' changed from 0664 (rw-rw-r--) to 0600 (rw-------)

Note: As well as thinking about file permissions on the key, you should also consider file permissions on disk backups.

Adding a passphrase

Setting restrictive file permissions is a good start, however if someone does manage to get read access to your key, they will be able to use it unless it's encrypted with a passphrase.

Setting a passphrase

When you create a new public/private key pair with ssh-keygen you will normally be prompted for a passphrase:

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/bob/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):

Alternatively, if you already have a private key you can encrypt the key with a passphrase using the -p option:

$ ssh-keygen -p -f ~/.ssh/id_rsa
Key has comment '/home/bob/.ssh/id_rsa'
Enter new passphrase (empty for no passphrase):

When doing this it's important to think about the complexity of the passphrase. If you use a simple passphrase, an attacker could easily guess the passphrase and unlock the key, assuming they have read access to the key in the first place. The ssh-keygen man page has the following advice on picking a passphrase:

Good passphrases are 10-30 characters long, are not simple sentences or otherwise easily guessable (English prose has only 1-2 bits of entropy per character, and provides very bad passphrases), and contain a mix of upper and lowercase letters, numbers, and non-alphanumeric characters.

As well as picking a good passphrase, it's also important to consider which key format you want to use.

Cracking passphrases and private key formats

Tools such as John the Ripper can be used to try to brute force a passphrase. This involves two steps, the first step is to extract the hash using ssh2john:

$ ./ssh2john.py /home/bob/.ssh/id_rsa > id_rsa.hash

Once the hash has been extracted, john can be used to try to brute force the passphrase:

$ ./john id_rsa.hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])

After loading the hash john will start to guess the passphrase. At the time of writing the default encryption algorithum for SSH keys is MD5/3DES. With a single CPU core john is able to make just under one million guesses per second:

0g 0:00:00:20  3/3 0g/s 899538p/s 899538c/s 899538C/s berete1

If you want to make an attacker's life difficult you should consider using the new private key format which was introduced in OpenSSH 6.4. This key format uses a bcrypt KDF to protect the key which is significantly slower to brute force. Using john on the same CPU I was only able to make just over 7 guesses per second:

0g 0:00:00:07  1/3 0g/s 7.627p/s 7.627c/s 7.627C/s rsaid

You can switch your key to the new format with the following command:

ssh-keygen -o -p -f ~/.ssh/id_rsa

Note: the -a option can also be used to specify how many rounds should be used.

Rotating keys

The last thing to consider is rotating your keys. This effectively boils down to the following steps:

  1. Generate a new private key.
  2. Copy the corresponding public key to each remote location you want to authenticate with.
  3. Check you can access each location using your new private key.
  4. Remove your old public key from all remote locations.
  5. Finally remove your private key.

How easy or difficult this will be depends on how many different places you've setup your public key. A very simple example might look something like the following:

# Generate a new private key
ssh-keygen -f ~/.ssh/id_rsa_new

# Copy your new public key to the remote hosts
ssh-copy-id -i ~/.ssh/id_rsa_new remote-box

# Verify you can use the new key to login
ssh -i ~/.ssh/id_rsa_new remote-box hostname

# Remove the old public key
ssh -i ~/.ssh/id_rsa_new remote-box 'sed -i "/O1qPS18Ni8F8GqZ0yEjzp665tTPR9F/d" ~/.ssh/authorized_keys'

# Delete and replace the old key
rm ~/.ssh/id_rsa ~/.ssh/id_rsa.pub
mv ~/.ssh/id_rsa_new ~/.ssh/id_rsa
mv ~/.ssh/id_rsa_new.pub ~/.ssh/id_rsa.pub