Sunday, November 6, 2016

Creating SSH private keys (advice as of 11/2016)

You have probably heard that using SSH keys for authentication is more secure than using usernames and passwords. I'm a total n00b at this, so take all of this advice with a grain of salt. If you find i'm giving bad advice, please let me know in the comments.

SSH keys are made in pairs - the public (which whatever wants to authenticate you must have) and the private (which only you should have). To create the SSH keys, you use a command called ssh-keygen . This is part of the OpenSSH software released by the OpenBSD Project, supported by the OpenBSD Foundation. Most nix style operating systems use this software, which is amazing considering how small the OpenBSD community is - small, yet technically deep and experienced :)

Anyways, I was trying to find some guidelines on creating secure SSH keys. Most Google documents that turned up are outdated. From what little I've learned reading I came away with:

1) RSA: at least 2048, but this is the least secure of the modern options
2) Use bcrypt
3) Use the new key format in OpenSSH with option -o

From the documentation:

-o


Causes ssh-keygen to save private keys using the new OpenSSH format rather than the more compatible PEM format. The new format has increased resistance to brute-force password cracking but is not supported by versions of OpenSSH prior to 6.5. Ed25519 keys always use the new private key format.


So, let's see what happens on my freshly installed and patched Ubuntu 16.04 LTS laptop:

Let's make sure we are on a modern version

~$ ssh -V
OpenSSH_7.2p2 Ubuntu-4ubuntu2.1, OpenSSL 1.0.2g  1 Mar 2016

So, what are the current default options? I ran the ssh-keygen command without parameters and accepted all defaults, including leaving the passphrase blank:

~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa):
Created directory '/home/user/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/user/.ssh/id_rsa.
Your public key has been saved in /home/user/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:[lots of characters here] user@laptophostname
The key's randomart image is:
+---[RSA 2048]----+
[lots of ASCII characters here]
+----[SHA256]-----+

Seems if we just execute ssh-keygen with no options and press enter, we get a 2048 RSA key with a SHA256 cipher.

We can now run "ls -a" and see that a new .ssh directory was created. Inside it are two keys, our private (which only we should see from now on) and a .pub , the public matching pair that we would install where we want to have remote access:

~/.ssh$ ls
id_rsa  id_rsa.pub

~/.ssh$ cat id_rsa
-----BEGIN RSA PRIVATE KEY-----
[lots of characters here]

~/.ssh$ cat id_rsa.pub 
ssh-rsa [lots of characters here] user@laptophostname

So we see that in the public key, we are sharing some information about us, that we probably want to avoid - even if this is meant to be a public key, we don't want to give more detail than needed to a potential attacker.

We can check the details of an existing key with

~/.ssh$ ssh-keygen -l -f keyfile_name
2048 SHA256:[lots of characters here] user@laptophostname (RSA)

One thing to note is that the output of this previous command is the same for the public or private key -  this means that it's useful to make sure they are indeed a pair!

Ok, fun enough, but let's delete this folder and try to create a more secure key.

~$ rm -r ./.ssh/

The current recommendation is to use ed25519 type keys instead of RSA. This is done with the -t option:

ssh-keygen -t ed25519

Note that when we explore the resulting files, the names and contents are a bit different

~/.ssh$ cat id_ed25519.pub 
ssh-ed25519 [lots of characters here] user@laptophostname

~/.ssh$ cat id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----

~/.ssh$ ssh-keygen -l -f id_ed25519.pub 
256 SHA256:[lots of characters here] user@laptophostname (ED25519)

We're supposedly being a bit more secure now. Note that per the documentation, ed25519 is always bit size 256 so there's no point in specifying an option with -b. Also, the new format is used by default with this key type, so the -o parameter isn't needed either.

We can still secure this further. First, let's remove our username and laptop name with -C and a comment inside quotes.

~$ ssh-keygen -t ed25519 -C "cat"
Generating public/private ed25519 key pair.

~/.ssh$ ssh-keygen -l -f id_ed25519.pub 
256 SHA256:CKhXQLlEa68uLCP9KRAhI9Bmg+0tRdEb47YuO2thuWU cat (ED25519)

Good - we can see that the string I gave as a comment is now shown in place of my username@hostname. Ideally you would put something in here that will help you use this key in the future. Most people create different keys for different services.

Now, let's make it so a brute force attack is tougher on our private key by adding computational rounds with the -a option. Apparently this is a protection we can specify that will future-proof our private keys a bit in case they fall into the wrong hands.



-a rounds
When saving a new-format private key (i.e. an ed25519 key or any SSH protocol 2 key when the -o flag is set), this option specifies the number of KDF (key derivation function) rounds used. Higher numbers result in slower passphrase verification and increased resistance to brute-force password cracking (should the keys be stolen).

I've seen several people online recommend at least 1000 rounds, so this would be the final command:

~$ ssh-keygen -t ed25519 -C "cat" -a 1000

Note that I'm allowing the program to prompt for a pass phrase instead of specifying one - this makes it so my pass phrase is not stored in the shell log. A note on the passphrase - you basically want a long, character based pass phrase, not a real English phrase.

At this moment we have generated a "pretty secure" SSH key. The next post will be on actually using them on a local server or in GitHub.

Some links apart from what I already put in the beginning that I found were among the better ones:

No comments:

Post a Comment