Vanja Ćosić

Rotate and upgrade your SSH keys

SSH is an invaluable tool for us developers and sysadmins, and it continues to be perhaps the most secure way to access and operate remote machines, despite being introduced almost 30 years ago.

The time came for me to rotate my client keys again and this time I decided write down why and how to do so. I hope it helps someone else.

Why rotate SSH keys? #

When establishing a connection to a server through SSH, your computer authenticates itself to the host through public-key cryptography using a key pair. Like most other types of credentials, it is good security hygiene to regurlarly review and rotate your key pairs to maintain a strong security posture.

Maybe you, like many others, have been moving the same SSH keys from one device to another for a decade to avoid the hassle of configuring everything again. Or maybe you experienced a catastrophic failure and restored a backup of your old computer to a new one. Meaning the keys could still be lying around on old laptops, company devices, or unencrypted disks.

Importantly, SSH keys are meant to be per-host, ie. a separate pair of keys for each device you use. So when you get a new computer you should generate a new key pair and configure access to each server or service you use. But in my experience, most developers do not realize this (including myself for a long time). And if they are aware, many still choose to not follow the practice because of the perceived hassle.

Some people even use folder syncing services like Dropbox or rsync to copy the same key pair to multiple devices. You really shouldn’t do that.

Maybe you’ve only ever had one key pair on the same device for many years, but it was generated back when the recommended key size was a lot smaller. These days the default size for new RSA keys is 3072 bits, while the recommended size is 4096 bits. So if your key is, say 1024 bits or fewer, then it’s also time for an upgrade.

Why switch to a different type of key? #

While we usually just refer to them as “SSH keys”, that is just a generic term for different types of key pairs based on various cryptographic algorithms. One doesn’t need understand or even know about the cryptography or mathematics behind these algorithms, we can instead rely on the recommendations from industry professionals.

Let’s look at the main types available in our local SSH client1:

Since many of us use git over SSH for our daily work, it is worth noting that GitHub dropped support for DSA keys and RSA keys used with a dated signature algorithm (SHA-1) in late 2021. And since March 2022 only ECDSA, Ed25519, and RSA (with SHA-2 signatures) are supported.10

Which type to choose? #

To sum up the previous section, you could create a fresh set of RSA keys and be reasonably secure, as long as is 4096 bits or more and uses SHA-2. One could even argue that it is overkill for our daily development work.

But security is a moving target and modern elliptic curve cryptography offers quite a few advantages that you might as well take advantage of when generating new keys. So for the rest of this post I will walk you through how to generate a new key pair based on Ed25519 and how to use it to connect to GitHub.

Which type are your current keys? #

Check the folder #

Navigate to the default SSH folder and list out all the files:

Terminal
$ cd ~/.ssh
$ ls -l
config        # Your SSH configuration file
id_rsa        # Your SSH private key (RSA)
id_rsa.pub    # Your SSH public key (RSA)
known_hosts   # List of servers you 'trust'

The default filename for new key pairs is id_, followed by the key type. For example: id_rsa.pub, id_ecdsa.pub, or id_ed25519.pub - meaning RSA, ECDSA, and Ed25519 type keys respectively.

🔑 This might seem obvious, but always bears repeating:
The file with the extension .pub is your PUBLIC key, which is the one you copy to remote machines or paste into services like GitHub, AWS, etc. The one without a file extension (like id_rsa), is your PRIVATE key which you must NEVER copy or share anywhere.

Ask the agent #

As shown above, it is common to look in the ~/.ssh folder on your computer to find your keys, but there might be SSH keys in use that are located elsewhere.

So let’s ask the ssh-agent which keys it knows about, using the list option:

Terminal
ssh-add -l

The command prints a list of keys and their details in the following format:

Terminal
$ ssh-add -l
# Length  Fingerprint                   Comment         Type
  4096    SHA256:R4DFsSQ23wSpKDcAfE...  user@hostname   (RSA)

Which tells us that the key in this example is a 4096-bit RSA key.

(This is assuming that the agent is running and working correctly. If it is not working for you, take a look at this article: Generating a new SSH key and adding it to the ssh-agent | GitHub Docs).

Archive the old keys #

You can have as many key pairs on your computer as you want and it is even prudent to use different key pairs for different contexts. But as time passes it is easy to forget which keys were made when and for what purpose.

So I recommend that you archive all your current ones, generate a new key pair, and then later generate purpose-specific ones as the need arises.

Back up #

We don’t want to simply delete the old keys right away, because we could get accidentally locked out. By renaming and moving the keys first, we can gradually migrate away without the risk of losing access.

You can back up the entire folder using the archive option of the copy utility:

Terminal
cp --archive ~/.ssh ~/ssh_backup

The result is a full copy of the folder, hopefully with permissions and everything intact in case you need later.

Rename #

If you already have an existing id_ed25519 key pair, rename the files to something else to avoid confusion and conflicts, for example:

Terminal
# Rename existing keys to have the prefix "old_"
mv id_ed25519 old_id_ed25519
mv id_ed25519.pub old_id_ed25519.pub

Inform the agent #

As mentioned earlier, the SSH agents keeps track of the keys in use on your computer. But unless configured to do so, it does not automatically react to changes you make to the key files.

So once you have archived and renamed your existing key(s), you need to remove them from the SSH agent.

You can do so using the -D or -d options in ssh-add:

Terminal
# Remove all known keys pairs
ssh-add -D

# Remove a single known key pair
ssh-add -d ~/.ssh/id_rsa

Generate a new key pair #

Unless you know what you are doing, you should not generate key pairs without a passphrase. So first, generate a very strong passphrase in your password manager of choice and copy it.

Then, generate a new SSH key pair:

Terminal
ssh-keygen -t ed25519 -a 100 -C "laptop"

Explanation of the options:

When you run the command, you have to choose where to put the file (default is fine) and enter a passphrase for the key (paste the one you generated earlier):

Terminal
$ ssh-keygen -t ed25519 -a 100 -C "laptop"
Generating public/private ed25519 key pair.
Enter file in which to save the key (~/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ~/.ssh/id_ed25519
Your public key has been saved in ~/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:9f32ks02FW2FwOPiDf3F02Rd2VkAksogx2Cb1auIHKUSv8 laptop

Update your config #

Make sure to update your SSH configuration to use the new identity. To update it, you can open the ~/.ssh/config file in your editor of choice.

Brief warning about using * in your config:
It is tempting to use a wildcard like Hosts * in your SSH config file. It is very common in tutorials and shared configurations and seems like a nice convenience. But you SHOULD NOT be using * unless you really know what you are doing. Malicious servers can abuse it to list all your public keys. And using a wildcard in combination with SSH forwarding leaves you vulnerable to attacks like SSH Forward Agent exploitation. Instead, you should set a Hosts configuration for each trusted server and point to the public key to use, like I’ve done for GitHub below.

Here is an example of how to set a configuration for GitHub:

~/.ssh/config
Host github.com  
  User git
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

Inform the agent, again #

The SSH agent needs to be told about new keys manually, we can do so using ssh-add:

Terminal
ssh-add id_ed25519

You might need the --apple-use-keychain option if you are on MacOS.

Then, use the list option to verify that the new key is listed and the old ones are no longer there:

Terminal
$ ssh-add -l
SHA256:9f32ks02FW2FwOPiDf3F02Rd2VkAksogx2Cb1auIHKUSv8 laptop

Test the new key #

To test the new key, you can use a service that offers SSH access like GitHub.

  1. Follow the steps in their guide, to associate your key with your account: Adding a new SSH key to your GitHub account - GitHub Docs

  2. Connect to GitHub to test that the key is working:

Terminal

Authenticity and fingerprints #

When connecting, you may see a notice about the authenticity of the server. Make sure that the fingerprint you are seeing in the response, matches one of the SSH key fingerprints GitHub has published publicly: GitHub’s SSH key fingerprints - GitHub Docs

So for example:

Terminal
$ ssh -T [email protected]
The authenticity of host 'github.com (140.82.121.4)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

At the time of writing, the fingerprint in the SSH response (SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU) matches the one GitHub has published for Ed25519 on their site. So I can reasonably conclude that I am in fact connecting to the real GitHub and not an imposter. I accept the connection by typing yes.

If everything has gone as planned, you should see this response:

Terminal
$ ssh -T [email protected]
Hi vanjacosic! You've successfully authenticated, but GitHub does not provide shell access.

Congrats, you have successfuly rotated and upgraded your SSH keys to keep up with the latest standard in security and performance!


  1. You can list the available key types in your local client using: ssh -Q key. For this post I am using the default OpenSSH version that ships with MacOS Ventura. ↩︎

  2. DSA Is Past Its Prime | Cryptography Dispatches by Filippo Valsorda ↩︎

  3. From Section 4 in FIPS 186-5: Digital Signature Standard, published in 2023:
    “This standard no longer approves the DSA for digital signature generation.” ↩︎

  4. See more about key length and the estimations of how long they will stay secure at Keylength.com↩︎

  5. Accusations of untrustworthy algorithms and interference from intelligence services goes back many years. Read more: How a Crypto ‘Backdoor’ Pitted the Tech World Against the NSA | WIRED by Kim Zetter. ↩︎

  6. Story about the presentation from 2010: PS3 hacked through poor cryptography implementation | Ars Technica by Casey Johnston. ↩︎

  7. Read more about Ed25519 on Wikipedia and SafeCurves.cr.yp.to and Ed25519.cr.yp.to↩︎

  8. Let Soatok be your guide to the various choices in: Guidance for Choosing an Elliptic Curve Signature Algorithm in 2022 | Dhole Moments↩︎

  9. If you are curious about the technical details, I highly recommend: A Deep dive into Ed25519 Signatures by Cendyne. ↩︎

  10. Improving Git protocol security on GitHub | The GitHub Blog ↩︎