Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A setup guide to use a personal gpg key for ssh authentication

GPG - SSH setup

Generating the master key

Here we create the master key. We want only Certify capability: we use the master key only to create the subkeys, Sign - Encrypt - Authenticate capabilities will be assigned to the subkeys.

Run the following command to start the master key generation process. Select the set your own capabilities creation process (type 8)

  ▶ gpg --full-generate-key --expert
  gpg (GnuPG) 2.2.9; Copyright (C) 2018 Free Software Foundation, Inc.
  This is free software: you are free to change and redistribute it.
  There is NO WARRANTY, to the extent permitted by law.

  Please select what kind of key you want:
  (1) RSA and RSA (default)
  (2) DSA and Elgamal
  (3) DSA (sign only)
  (4) RSA (sign only)
  (7) DSA (set your own capabilities)
  (8) RSA (set your own capabilities)
  (9) ECC and ECC
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (13) Existing key
  Your selection? 8

Disable sign capability (type S)

  Possible actions for a RSA key: Sign Certify Encrypt Authenticate
  Current allowed actions: Sign Certify Encrypt

  (S) Toggle the sign capability
  (E) Toggle the encrypt capability
  (A) Toggle the authenticate capability
  (Q) Finished

  Your selection? S

Disable encrypt capabilities (type E)

  Possible actions for a RSA key: Sign Certify Encrypt Authenticate
  Current allowed actions: Certify Encrypt

  (S) Toggle the sign capability
  (E) Toggle the encrypt capability
  (A) Toggle the authenticate capability
  (Q) Finished

  Your selection? E

Quit and continue the creation process (type Q)

  Possible actions for a RSA key: Sign Certify Encrypt Authenticate
  Current allowed actions: Certify

  (S) Toggle the sign capability
  (E) Toggle the encrypt capability
  (A) Toggle the authenticate capability
  (Q) Finished

  Your selection? Q

Input the desired key size of the master key

  RSA keys may be between 1024 and 4096 bits long.
  What keysize do you want? (2048) 4096
  Requested keysize is 4096 bits

Setup the expiration for the master key

  Please specify how long the key should be valid.
        0 = key does not expire
        <n>  = key expires in n days
        <n>w = key expires in n weeks
        <n>m = key expires in n months
        <n>y = key expires in n years
  Key is valid for? (0) 1y
  Key expires at Sat Jul 27 17:59:59 2019 BST
  Is this correct? (y/N) y

Construct your user ID (input your full name and email, leave comment empty). Type O to complete

  GnuPG needs to construct a user ID to identify your key.

  Real name: Mattia Cattarinussi
  Email address: example@email.com
  Comment:
  You selected this USER-ID:
  "Mattia Cattarinussi <example@email.com>"

  Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

Enter a passphrase for your master key

  ┌──────────────────────────────────────────────────────┐
  │ Please enter the passphrase to                       │
  │ protect your new key                                 │
  │                                                      │
  │ Passphrase: ________________________________________ │
  │                                                      │
  │       <OK>                              <Cancel>     │
  └──────────────────────────────────────────────────────┘

If you did the above steps correctly you should have the following result

  We need to generate a lot of random bytes. It is a good idea to perform
  some other action (type on the keyboard, move the mouse, utilize the
  disks) during the prime generation; this gives the random number
  generator a better chance to gain enough entropy.
  gpg: key 4EF9EF4CDBD7AB1B marked as ultimately trusted
  gpg: revocation certificate stored as '/Users/mattiacattarinussi/.gnupg/openpgp-revocs.d/F8DD1C85581AB87675EF97444EF9EF4CDBD7AB1B.rev'
  public and secret key created and signed.

  pub   rsa4096 2018-07-27 [C] [expires: 2019-07-27]
        F8DD1C85581AB87675EF97444EF9EF4CDBD7AB1B
  uid                      Mattia Cattarinussi <example@email.com>

Store the revocation certificate (created by gpg) for your master key on a physical device.

Generate Sign, Encrypt and Authentication subkeys

Run this command to edit your key

  ▶ gpg --expert --edit-key mattia
  gpg (GnuPG) 2.2.9; Copyright (C) 2018 Free Software Foundation, Inc.
  This is free software: you are free to change and redistribute it.
  There is NO WARRANTY, to the extent permitted by law.

  Secret key is available.

  sec  rsa4096/4EF9EF4CDBD7AB1B
  created: 2018-07-27  expires: 2019-07-27  usage: C
  trust: ultimate      validity: ultimate
  [ultimate] (1). Mattia Cattarinussi <example@email.com>

In the gpg console prompt specify that you want to add a new key for that master key:

  gpg> addkey

Select the set your own capabilities creation process (type 8)

  Please select what kind of key you want:
  (3) DSA (sign only)
  (4) RSA (sign only)
  (5) Elgamal (encrypt only)
  (6) RSA (encrypt only)
  (7) DSA (set your own capabilities)
  (8) RSA (set your own capabilities)
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (12) ECC (encrypt only)
  (13) Existing key
  Your selection? 8

Add Authenticate capabilities (type A). Sign and Encrypt capabilities are already enabled by default

  Possible actions for a RSA key: Sign Encrypt Authenticate
  Current allowed actions: Sign Encrypt

  (S) Toggle the sign capability
  (E) Toggle the encrypt capability
  (A) Toggle the authenticate capability
  (Q) Finished

  Your selection? A

Type Q to continue the process

  Possible actions for a RSA key: Sign Encrypt Authenticate
  Current allowed actions: Sign Encrypt Authenticate

  (S) Toggle the sign capability
  (E) Toggle the encrypt capability
  (A) Toggle the authenticate capability
  (Q) Finished

  Your selection? Q

Input the desired key size for the subkey

  RSA keys may be between 1024 and 4096 bits long.
  What keysize do you want? (2048) 4096
  Requested keysize is 4096 bits

Setup the expiration for the subkey

  Please specify how long the key should be valid.
        0 = key does not expire
        <n>  = key expires in n days
        <n>w = key expires in n weeks
        <n>m = key expires in n months
        <n>y = key expires in n years
  Key is valid for? (0) 1y
  Key expires at Sat Jul 27 18:19:38 2019 BST
  Is this correct? (y/N) y
  Really create? (y/N) y

Input the passphrase for the master key (the one you setup in the master key generation process). You should get this result

  We need to generate a lot of random bytes. It is a good idea to perform
  some other action (type on the keyboard, move the mouse, utilize the
  disks) during the prime generation; this gives the random number
  generator a better chance to gain enough entropy.

  sec  rsa4096/4EF9EF4CDBD7AB1B
  created: 2018-07-27  expires: 2019-07-27  usage: C
  trust: ultimate      validity: ultimate
  ssb  rsa4096/855B567AA2E43D00
  created: 2018-07-27  expires: 2019-07-27  usage: SEA
  [ultimate] (1). Mattia Cattarinussi <example@email.com>

Save and exit

  gpg> save

Now if you list your keys you will see also a subkey (sub) with SEA capabilities (Sign - Encrypt - Authenticate)

  ▶ gpg --list-keys
  /Users/mattiacattarinussi/.gnupg/pubring.kbx
  --------------------------------------------
  pub   rsa4096 2018-07-27 [C] [expires: 2019-07-27]
        F8DD1C85581AB87675EF97444EF9EF4CDBD7AB1B
  uid           [ultimate] Mattia Cattarinussi <example@email.com>
  sub   rsa4096 2018-07-27 [SEA] [expires: 2019-07-27]

Setup the gpg-agent for SSH authentication

Enable the gpg-agent ssh support

  ▶ echo enable-ssh-support >> $HOME/.gnupg/gpg-agent.conf

Set SSH_AUTH_SOCK so that SSH will use gpg-agent instead of ssh-agent. Add this to tour bashprofile or zshrc

unset SSH_AGENT_PID
if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
  export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"
fi
export GPG_TTY=$(tty)
gpg-connect-agent updatestartuptty /bye >/dev/null

Enable the gpg subkey for ssh authentication:

  • Get the subkey keygrip

    ▶ gpg --list-keys --with-keygrip
    
    /Users/mattiacattarinussi/.gnupg/pubring.kbx
    --------------------------------------------
    pub   rsa4096 2018-07-27 [C] [expires: 2019-07-27]
          F8DD1C85581AB87675EF97444EF9EF4CDBD7AB1B
          Keygrip = 22DBE374608C6220B321D3A2543F3806FF63A49D
    uid           [ultimate] Mattia Cattarinussi <example@email.com>
    sub   rsa4096 2018-07-27 [SEA] [expires: 2019-07-27]
          Keygrip = A55719832AF939C531BACFFABB2A47B52FFBBF43
    
  • Add the keygrip of your subkey in the list of approved keys

    ▶ echo A55719832AF939C531BACFFABB2A47B52FFBBF43 >> ~/.gnupg/sshcontrol
    

Check if the key is present in the ssh identities list

  ▶ ssh-add -l
  4096 SHA256:bCVzkgaoGSqJC89hZ/8gclTn7ENN/dJ+mZBBw2zJFuI (none) (RSA)

Retrieve the public ssh key for the subkey

  ▶ gpg --export-ssh-key mattia
  ssh-rsa <A_LOT_OF_STUFF_HERE> openpgp:0xA2E43D00

You can test if the key is working with your Github account. The ssh public key generated in the previous step has to be added to your Github SSH keys.

  ▶ ssh -T git@github.com
  Hi mcattarinussi! You've successfully authenticated, but GitHub does not provide shell access.

Backup keys and remove the master key

Export the secret master key

  ▶ gpg -a --export-secret-keys > master-secret-key.gpg

Export all the secret subkeys

  ▶ gpg -a --export-secret-subkeys > sub-secret-keys.gpg

Save master-secret-key.gpg and sub-secret-keys.gpg on a physical device.

Delete the secret keys (you need to delete all the subkeys as well)

  ▶ gpg --delete-secret-key mattia

Enter the master password and confirm the deletion in the subsequent confirmation dialogs.

Restore the subkeys

  ▶ gpg --import sub-secret-keys.gpg

Check the result

  ▶ gpg --list-secret-keys
  /Users/mattiacattarinussi/.gnupg/pubring.kbx
  --------------------------------------------
  sec#  rsa4096 2018-09-14 [C] [expires: 2020-09-13]
        5615F7C581E8450E34F9031703426E5D827D6A81
  uid           [ultimate] Mattia Cattarinussi <mcattarinussi@gmail.com>
  ssb   rsa4096 2018-09-14 [S] [expires: 2018-09-16]
  ssb   rsa4096 2018-09-14 [E] [expires: 2020-09-13]
  ssb   rsa4096 2018-09-14 [A] [expires: 2020-09-13]

The # after the master key means that the key is not stored locally.

References

@x10an14

This comment has been minimized.

Copy link

@x10an14 x10an14 commented Jan 4, 2021

I'm unable to get this to work =/

My set-up/keys
[2021-01-04 13:01:35] 0 x10an14@christian-lenovo-laptop:~
-> $ gpg --export-ssh-key 35E45B226C19D3C291E4C8385317C36F8180D94D
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIByOSG6PDkXXBjlEDgNYA1ovuHfAzOkDPEH+dwWPo513 openpgp:0x8180D94D
[2021-01-04 13:01:52] 0 x10an14@christian-lenovo-laptop:~
-> $ echo "KEYINFO --ssh-list --ssh-fpr=sha256" | gpg-connect-agent
S KEYINFO 8D5686987DCBF0E701C59ED4DAA4D1D68A753AB8 T D2760001240103040006154142240000 OPENPGP.1 - - SHA256:PAiSF7XHHCzzGuoxKFNAPKNQTea29NITZ3sthKoH1ig - S
S KEYINFO 3DE6052B0479F4C5A944737DD211B78ABE791981 T D2760001240103040006154142240000 OPENPGP.2 - - - - DS
OK
[2021-01-04 13:02:03] 0 x10an14@christian-lenovo-laptop:~
-> $ gpg --fingerprint --with-keygrip christian.chavez@nav.no
pub   ed25519 2020-12-11 [SCA] [expires: 2022-12-11]
      35E4 5B22 6C19 D3C2 91E4  C838 5317 C36F 8180 D94D
      Keygrip = 8D5686987DCBF0E701C59ED4DAA4D1D68A753AB8
uid           [ultimate] Christian Chavez <christian.chavez@nav.no>
sub   cv25519 2020-12-11 [E]
      Keygrip = 3DE6052B0479F4C5A944737DD211B78ABE791981

[2021-01-04 13:02:12] 0 x10an14@christian-lenovo-laptop:~
-> $ ssh-add -l
256 SHA256:PAiSF7XHHCzzGuoxKFNAPKNQTea29NITZ3sthKoH1ig (none) (ED25519)
[2021-01-04 13:02:42] 0 x10an14@christian-lenovo-laptop:~
-> $ ssh-add -L
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIByOSG6PDkXXBjlEDgNYA1ovuHfAzOkDPEH+dwWPo513 (none)
[2021-01-04 13:02:46] 0 x10an14@christian-lenovo-laptop:~
-> $

When I've uploaded the output of gpg --export-ssh-key 35E45B226C19D3C291E4C8385317C36F8180D94D to my SSH keys in github.com/settings/keys:

ssh -Tv git@github.com output:
[2021-01-04 13:02:46] 0 x10an14@christian-lenovo-laptop:~
-> $ ssh -T git@github.com
sign_and_send_pubkey: signing failed for ED25519 "(none)" from agent: agent refused operation
git@github.com: Permission denied (publickey).
[2021-01-04 13:03:30] 255 x10an14@christian-lenovo-laptop:~
-> $ ssh -Tv git@github.com
OpenSSH_8.2p1 Ubuntu-4ubuntu0.1, OpenSSL 1.1.1f  31 Mar 2020
debug1: Reading configuration data /home/x10an14/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to github.com [140.82.121.4] port 22.
debug1: Connection established.
debug1: identity file /home/x10an14/.ssh/id_rsa type -1
debug1: identity file /home/x10an14/.ssh/id_rsa-cert type -1
debug1: identity file /home/x10an14/.ssh/id_dsa type -1
debug1: identity file /home/x10an14/.ssh/id_dsa-cert type -1
debug1: identity file /home/x10an14/.ssh/id_ecdsa type -1
debug1: identity file /home/x10an14/.ssh/id_ecdsa-cert type -1
debug1: identity file /home/x10an14/.ssh/id_ecdsa_sk type -1
debug1: identity file /home/x10an14/.ssh/id_ecdsa_sk-cert type -1
debug1: identity file /home/x10an14/.ssh/id_ed25519 type -1
debug1: identity file /home/x10an14/.ssh/id_ed25519-cert type -1
debug1: identity file /home/x10an14/.ssh/id_ed25519_sk type -1
debug1: identity file /home/x10an14/.ssh/id_ed25519_sk-cert type -1
debug1: identity file /home/x10an14/.ssh/id_xmss type -1
debug1: identity file /home/x10an14/.ssh/id_xmss-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1
debug1: Remote protocol version 2.0, remote software version babeld-78794f53
debug1: no match: babeld-78794f53
debug1: Authenticating to github.com:22 as 'git'
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: rsa-sha2-512
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: Server host key: ssh-rsa SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8
debug1: Host 'github.com' is known and matches the RSA host key.
debug1: Found key in /home/x10an14/.ssh/known_hosts:1
debug1: rekey out after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: rekey in after 134217728 blocks
debug1: Will attempt key: (none) ED25519 SHA256:PAiSF7XHHCzzGuoxKFNAPKNQTea29NITZ3sthKoH1ig agent
debug1: Will attempt key: /home/x10an14/.ssh/id_rsa
debug1: Will attempt key: /home/x10an14/.ssh/id_dsa
debug1: Will attempt key: /home/x10an14/.ssh/id_ecdsa
debug1: Will attempt key: /home/x10an14/.ssh/id_ecdsa_sk
debug1: Will attempt key: /home/x10an14/.ssh/id_ed25519
debug1: Will attempt key: /home/x10an14/.ssh/id_ed25519_sk
debug1: Will attempt key: /home/x10an14/.ssh/id_xmss
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss>
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering public key: (none) ED25519 SHA256:PAiSF7XHHCzzGuoxKFNAPKNQTea29NITZ3sthKoH1ig agent
debug1: Server accepts key: (none) ED25519 SHA256:PAiSF7XHHCzzGuoxKFNAPKNQTea29NITZ3sthKoH1ig agent
sign_and_send_pubkey: signing failed for ED25519 "(none)" from agent: agent refused operation
debug1: Trying private key: /home/x10an14/.ssh/id_rsa
debug1: Trying private key: /home/x10an14/.ssh/id_dsa
debug1: Trying private key: /home/x10an14/.ssh/id_ecdsa
debug1: Trying private key: /home/x10an14/.ssh/id_ecdsa_sk
debug1: Trying private key: /home/x10an14/.ssh/id_ed25519
debug1: Trying private key: /home/x10an14/.ssh/id_ed25519_sk
debug1: Trying private key: /home/x10an14/.ssh/id_xmss
debug1: No more authentication methods to try.
git@github.com: Permission denied (publickey).
[2021-01-04 13:08:15] 255 x10an14@christian-lenovo-laptop:~
-> $

So it's obvious my (ssh?) gpg-agent tests the correct key, but I don't get any prompts for authenticating? I would expect that since its private-key currently residing on my connected yubikey, ref:

[2021-01-04 13:08:15] 255 x10an14@christian-lenovo-laptop:~
-> $ gpg --sign ~/.bashrc
Please unlock the card

Number: 0006 15414224
Holder: Christian Chavez
Counter: 3
PIN:
[2021-01-04 13:10:28] 0 x10an14@christian-lenovo-laptop:~
-> $ gpg --verify ~/.bashrc.gpg
gpg: Signature made Mon 04 Jan 2021 01:09:59 PM CET
gpg:                using EDDSA key 35E45B226C19D3C291E4C8385317C36F8180D94D
gpg: Good signature from "Christian Chavez <christian.chavez@nav.no>" [ultimate]
[2021-01-04 13:10:36] 0 x10an14@christian-lenovo-laptop:~
-> $

Any idea on what might be wrong? I suspect it isn't a misconfigured pin-entry setting due to the above gpg --sign working without problem (read: the PIN: is where it awaits my inputted PIN as expected).

@AmrithVengalath

This comment has been minimized.

Copy link

@AmrithVengalath AmrithVengalath commented Feb 13, 2021

Thanks it worked

@hedefalk

This comment has been minimized.

Copy link

@hedefalk hedefalk commented Feb 19, 2021

I'm having the same problem as @x10an14. Did you solve it?

@AmrithVengalath

This comment has been minimized.

Copy link

@AmrithVengalath AmrithVengalath commented Feb 19, 2021

I'm having the same problem as @x10an14. Did you solve it?

Yes it worked for me .

@morfeokmg

This comment has been minimized.

Copy link

@morfeokmg morfeokmg commented Jun 23, 2021

Thanks it worked

how do you resolve it?, do you have the steps?

@x10an14

This comment has been minimized.

Copy link

@x10an14 x10an14 commented Jun 26, 2021

@hedefalk: I'm having the same problem as @x10an14. Did you solve it?

Yeah, my two cents:

  • ignore the whole subkey ID in sshcontrol thing.
  • make sure you have specified absolute path to your pinentry of choice in your gpg-agent.conf.
  • don't put anything at all in sshcontrol.

Just make sure you have an authentication gpg subkey, and let gpg figure it out for you. Has worked for me for six months now except for one detail (when juggling multiple gpg keys): https://groups.google.com/g/git-users/c/eMZJ7Yd0O_s/m/fVsUFjZkAwAJ

@kepebi

This comment has been minimized.

Copy link

@kepebi kepebi commented Aug 23, 2021

Can gpg-agent remember passphrases of all keys? How to do it?

@George-Miao

This comment has been minimized.

Copy link

@George-Miao George-Miao commented Aug 24, 2021

Nice job! I've set up a yubikey as a PGP smartcard and using it to sign all my commits and auth for GitHub etc.! Thanks for the guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment