Replace SSH with GnuPG
Table of Contents
Below describes the process of using the gpg-agent as a drop-in replacement for the ssh-agent. It also has steps for importing your existing ssh keys into gpg. Reason for wanting to do this are:
- Reduced key maitainance. You no longer have any ssh keys, everything is inside gpg.
- Add the "ssh keys"—they will be gpg authentication keys after this tutorial—to a smartcard (not described here).
1 GPG as a ssh-agent
To make gpg act as a ssh-agent add these lines to your ~/.bashrc
:
export GPG_TTY=$(tty) unset SSH_AGENT_PID [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ] \ && export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
It may or may not be necessary to enable ssh support in your
~/.gnupg/gpg-agent.conf
:
echo 'enable-ssh-support' >> ~/.gnupg/gpg-agent.conf
3 Create SSH Keys with GPG
The easiest way to start using GPG as a replacement for SSH is to create new SSH keys. We will do this by adding an authentication subkey to an already existing GPG key. If you don't already have a GPG key, create one with:
gpg --full-gen-key
and follow the instructions. If you need help with generating a key pair read3.
To create an authentication subkey:
gpg --expert --edit-key <your-id>
- Enter
addkey
. - Choose
RSA (set your own capabilities)
(maybe 8). - The allowed actions of the subkey should be
authenticate
and nothing else. - The size of the key should be at least 2048, for the key to be considered secure for the foreseeable future.
- Choose an expiration date you like.
- Enter
quit
.
4 Export GPG Authentication Keys as SSH Public Keys
For every authentication key you want to use for ssh you need to add
their public key to ~/.ssh
so you can reference them in
~/.ssh/config
. You also need to add the gpg subkey fingerprints to
~/.gnupg/sshcontrol
so the gpg-agent knows which keys can be used
for ssh.
# Ensure the directory exists mkdir ~/.ssh # Select the fingerprint of a authentication subkey gpg -K --with-subkey-fingerprint # Note the ! after the fingerprint # Note this will overwrite ~/.ssh/<public-key> if it exists gpg --output ~/.ssh/<public-key> --export-ssh-key <fingerprint>! # Modify permission on the public key file to 600 chmod 0600 ~/.ssh/<public-key> # Add keygrips of auth subkeys to ~/.gnupg/sshcontrol gpg -K --with-keygrip echo <key-grip> >> ~/.gnupg/sshcontrol
4.1 Script
If you are too lazy to enter all the above commands into the terminal then you can this script.
set -e confirm_info () { gpg -K --with-subkey-fingerprint --with-keygrip "${uid}" cat << EOF Fingerprint: ${fingerprint} Keygrip: ${keygrip} Public key file: ${file_pk} Is this correct (y/N): EOF read -r confirm [ "${confirm}" = y ] && return 0 return 1 } mkdir -p ~/.ssh while true; do gpg -K cat << EOF Which uid to export as SSH key, this will also be used as the filename of the public key in ~/.ssh (or "q" to finish): ' EOF read -r uid [ "${uid}" = q ] && break file_pk="${HOME}/.ssh/${uid}".pub gpg -K --with-subkey-fingerprint "${uid}" echo 'What is the fingerprint of the authentication *subkey* to export as SSH key: ' read -r fingerprint keygrip="$(gpg -K --with-subkey-fingerprint --with-keygrip "${uid}" | awk "/${fingerprint}/ {getline; print \$3}")" if [ "${#keygrip}" -ne 40 ]; then # keygrips are always 40 characters long gpg -K --with-subkey-fingerprint --with-keygrip "${uid}" echo "Could not automatically find the keygrip of subkey with ${fingerprint}, please enter it manually: " read -r keygrip fi if [ -f "${file_pk}" ]; then echo "File ${file_pk} already exists." else confirm_info || continue gpg --output "${file_pk}" --export-ssh-key "${fingerprint}"! chmod 0600 "${file_pk}" echo "${keygrip}" 0 >> "${GNUPGHOME:-~/.gnupg}"/sshcontrol fi done exit
5 Setup ssh_config and git-config for Multiple SSH Keys
5.1 Simple Configuration
Now you can use the public keys you just created in your
~/.ssh/config
to let ssh know when to use which key. A simple
configuration could look like:
Host gitlab.com IdentityFile ~/.ssh/gitlab.pub PreferredAuthentications publickey Host github.com IdentityFile ~/.ssh/github.pub PreferredAuthentications publickey
This will make ssh
use the gitlab.pub
key when the host is
gitlab.com and github.pub
when the host is github.com.
5.2 Multiple Github Accounts
When you have multiple account at the same host and you can use the
same key e.g. github.com then the above configuration will not work.
You will need to modify your ~/.config/git/config
and
~/.ssh/config
to make this setup work. It also requires a specific
directory structure.
This directory structure is necessary so git can be configured to use the correct ssh command and other options if required.
projects-directory ├── github-account-1 │ ├── project-1 │ ├── project-2 ├── github-account-2 │ ├── project-3 │ ├── project-4
5.2.1 Setup ssh_config
~/.ssh/config
#+begin_src conf-unix -n
Host github.com-account1 User git HostName github.com IdentityFile ~/.ssh/github1.pub PreferredAuthentications publickey
Host github.com-account2 User git HostName github.com IdentityFile ~/.ssh/github2.pub PreferredAuthentications publickey #+end_src unix
For the second account, the github.com-account2
is just an alias and
the User
and HostName
are what ssh actually uses as user and host
i.e. ssh User@HostName
. We can now take advantage of this alias in
the ~/.config/git/config
.
5.2.2 Setup git-config
~/.config/git/config
#+begin_src conf-unix -n
[user]
name = Your Name
useConfigOnly = true [init] defaultBranch = main
[includeIf "gitdir:~/projects-directory/github1/"] path = ~/.config/git/config.github1 [includeIf "gitdir:~/projects-directory/github2/"] path = ~/.config/git/config.github2 #+end_src unix
The includeIf
allows git to load settings based on the location of
the project. So earch github account will get its own git config file
with the location specified in the path
.
github1
is the default account so it is basically the same as in the
simple configuration except here commit signing is turned on, since
you have a gpg key you might as well sign your commits. The gpg email
can be found with gpg -K
.
Note: to sign git commits and have them verified by github your gpg public key must added to your github account and the gpg email must be the same as the email used in github. You can use the no-reply email if you want to keep your email private.
~/.config/git/config.github1
#+begin_src conf-unix -n
[user]
email = github1@gmail.com
signingKey = <gpg-key-id>
[url "git@github.com-account1"]
insteadOf = git@github.com
[commit]
gpgsign = true
[tag]
gpgsign = true
#+end_src unix
~/.config/git/config.github2
#+begin_src conf-unix -n
[user]
email = github2@gmail.com
signingKey = <gpg-key-id>
[url "git@github.com-account2"]
insteadOf = git@github.com
[commit]
gpgsign = true
[tag]
gpgsign = true
#+end_src unix
Note that the github.com-account1
and github.com-account2
part
of the urls are the same as the host aliases defined in
~/.ssh/config
earlier. This will make sure that ssh will use the
correct ssh key. This only have when inside
~/projects-directory/github1
or ~/projects-directory/github2
.
6 Add SSH and GPG Keys to Github/lab
This step depends on which platform you use, so read the respective docs or search the answer on duckduckgo.
7 Errors
If you get an error like agent refused operation
either try changing
pinentry progams in ~/.gnupg/gpg-agent.conf
or add this to your
shell config file, e.g. ~/.bashrc
:
gpg-connect-agent UPDATESTARTUPTTY /bye
This will ensure that the gpg-agent is started for ssh support.
8 Sources
- How to enable SSH access using a GPG key for authentication
- How to import your existing SSH keys into your GPG key
- Import ed25519 ssh keys into GPG
- Multiple Identity Git Config
- Multiple Github Accounts SSH/GPG setup
- Force the use of a gpg-key as an ssh-key for a given server
- Arch Wiki: SSH agent
- Arch wiki: GnuPG usage