Though it is generally the case and is considered a good practice to have only one public-private key pair per device, sometimes you might need to use more than one. You might use an SSH key pair for your personal projects and a different one for work repositories. On top of that, and you could be using another key to access the client's server.
Managing SSH keys can become cumbersome as soon as the need to use a second key arises. Traditionally, you would use ssh-add
to add your keys to ssh-agent
, typing in the password for each key as needed. The problem, however, is that this needs
to be done every time the computer is restarted since ssh-agent
will also restart and the keys have to be added along with typing in the
passwords for each key all over again.
Fortunately, the OpenSSH client offers the possibility to manage several keys. The solution for this is to automate adding and storing keys and to specify which key to use which repository by creating a configuration file to specify the customizations. For this purpose, the file ~/.ssh/config
with details about hosts using different keys (called "identities") is used.
SSH Config
SSH Config is a per-user configuration file for SSH communication.
Create a new file, if you don't already have one, at ~/.ssh/config
. If this config file is new, you might have to do chmod 600 ~/.ssh/config
for permissions.
On Linux and macOS, verify that the permissions on your IdentityFile are 400. SSH will reject, in a not very explicit manner, the SSH keys that are too readable. Tighten it a bit by:
chmod 400 ~/.ssh/id_ed25519
The Ed25519 was introduced on OpenSSH version 6.5 and is now the recommended way to generate ssh key pair. This is EdDSA implementation using the Twisted Edwards curve. It uses elliptic curve cryptography that offers better security with faster performance compared to DSA or ECDSA. Today, the RSA is the most widely used public-key algorithm for SSH keys. But compared to Ed25519, it's slower and even considered not safe if it's generated with a key smaller than 2048-bit length.
The Ed25519 public-key is compact, contains 68 characters compared to RSA 3072 that has 544 characters. Generating the key is also almost as fast as the signing process. It's also fast to perform batch signature verification with Ed25519. It is built to be collision resilience. Hash-function collision won't break the system.
$ ssh-keygen -t ed25519 -C "your_email@example.com"
This creates a new ssh key, using the provided email as a label. If your system doesn't support the Ed25519 algorithm, use:
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
I have two keys,
id_25519.pub
for work repositories andid_25519_personal.pub
for my personal projects.
Forward, the first problem to solve using this config
is to avoid having to add custom-named SSH keys using ssh-add
. Assuming your private key is named ~/.ssh/id_ed25519
, add the following to the config
file:
Host github.com
HostName github.com
User github-corporate-user # GitHub username
AddKeysToAgent yes
UseKeychain yes
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_ed25519
The base URI of Host github.com
in config
and the base URI of the repository that you want to work with must match.
Next, the second key for your personal projects with a custom hostname:
Host github-personal
HostName github.com
User github-personal-user # GitHub username
UseKeychain yes
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
AddKeysToAgent yes
Note the identitiesOnly yes
in the above setting. This is because the configuration alone does not ensure that the specified key is used. This is because the ssh-agent
has its own logic as to which key is used and the IdentityFile
information only adds these keys to the list of possible keys.
To restrict the selection to the key specified here, you need the set the IdentitiesOnly
- either host-specific or for all hosts. In the latter case, however, a key must be specified explicitly for each host like it is done here.
Test your connection
$ ssh -T git@github.com
$ ssh -T git@github-personal
With each command, if you've used RSA keys, you may see this kind of warning, type yes:
The authenticity of host 'github.com (192.30.252.1)' can't be established.
RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:
Are you sure you want to continue connecting (yes/no)?
If everything is OK, you will see these messages:
Hi your_name! You've successfully authenticated, but GitHub does not provide shell access. Hi your_name! You've successfully authenticated, but GitHub does not provide shell access.
Anyways, with the configuration in place, now you can git clone git@github-personal:github-account/interesting-project.git
.
If you had already cloned the repository, you can change the remote url, if you want, by:
git remote set-url origin git@github-personal:github-account/interesting-project.git git@github.com:some-github-user/interesting-project.git
Changes URLs for the remote. Sets first URL for remote
that matches regex (first URL if no is given) to . If doesn’t match any URL, an error occurs and nothing is changed. More
Configure Git Identity
You can configure an individual repo to use a specific user / email address which overrides the global configuration. From the root of the repo, run:
$> git config user.name "Your Name"
$> git config user.email "your@email.com"
Or you could edit the config file to add the user identity directly $> git config --local -e
and add the following:
[user]
name = Your Name
email = your@email.com
If you require different emails to be used for different repositories, from Git v2.13
you can set the email on a directory basis by editing the global config ~/.gitconfig
using conditionals like so:
[user]
name = Your Name
email = your@email.com
[includeIf "gitdir:~/private/"]
path = ~/private/.gitconfig
And then your private config at ~/private/.gitconfig
would look like:
[user]
email = other@email.com
Per repository settings
It turns out you can also define repository-specific keys overriding the configuration in ~/.ssh/config
file. There
are, at least, two ways to do this.
One, using the GIT_SSH_COMMAND. From Git version 2.3.0, you can use the environment variable GIT_SSH_COMMAND like this:
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa" git clone git@github-corporate:company/project.git
Note that -i can sometimes be overridden by your config file, in which case, you should give SSH an empty config file, like this:
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_rsa -F /dev/null" git clone git@github-corporate:company/project.git
Second, via core.sshCommand. From Git version 2.10.0, you can configure this per repo or globally, so you don't have to set the environment variable anymore!
git config core.sshCommand "ssh -i ~/.ssh/id_rsa -F /dev/null"
git pull
git push