Encryption Part 4 - Sign Git Commits and SSH with PIV

Image: Brandon Kalinowski

Brandon Kalinowski

In part three I detailed how to create a secure environment and then how to create a master GPG key and then create signing, encrypting, and authentication subkeys that exist only on the smart card. I then detailed how to install Keybase and then import this externally generated PGP public key.

In the fourth and final installment of this encryption series, I will explain:

  • How you can now use this new PGP key to sign git commits
  • How to import public keys and smart card stubs onto another computer
  • How to use the smart card for SSH authentication using PIV

Using GPG to Sign Git Commits

It is very easy to spoof who made a commit with git, by simply changing the email. If you were to then push this commit to GitHub, GitHub would then associate that commit with the other account as users are only identified by an email address in Git. Consider what would happen if a bad actor pushed a security hole into the source code of your team's software project and made it look like you added that code. That wouldn't be very good.

So how is this rectified? Well, you can make it a practice to always sign your git commits. If your private key exists only on your smart card, making a push to the git server would require (a) inserting the smart card, and (b) unlocking it with the PIN. The smart card locks after three unsuccessful PIN attempts so even if the smart card were stolen, and your git login details were stolen and the stolen key was not yet revoked it would be impossible to brute force the PIN to unlock the card. That is a solid amount of security!

To use our newly installed GPG key to sign all git commits it is quite simple:

$ gpg --list-secret-keys --keyid-format LONG
sec#  rsa4096/E870EE00 2018-05-01 [SC]
      Key fingerprint = <FINGERPRINT>
uid                 [ultimate] Jim Smith <your@email.com>
ssb>  rsa4096/F9E3E72E 2018-05-01 [E]
ssb>  rsa4096/F9E3E725 2018-05-01 [S]
ssb>  rsa4096/F858E768 2018-05-01 [A]

$ git config --global user.signingkey E870EE00
$ git config --global commit.gpgsign true

Now you can import the key into GitHub:

open https://github.com/settings/keys
# Click "New GPG key"
keybase pgp export | pbcopy

You can now paste the key and save.

You should now be able to sign commits from the command line. You will be asked to insert the smart card key and enter the PIN. The PIN will be cached until the key is removed so you don't have to continually enter it.

Sign Commits from Tower

If you use Tower for git operations there are a few extra steps required:

Add no-tty to your GPG configuration, to allow Tower to use it:

echo no-tty >> ~/.gnupg/gpg.conf

And then configure Tower to use the correct gpg executable:

git config --global gpg.program $(which gpg)

Using GPG

Enabling no-tty is necessary for Tower to work with gpg signing. Unfortunately, this breaks gpg commands in the command line. If you have an issue with "no terminal at all requested," comment out the line no-tty from ~/.gnupg/gpg.conf. Once you are done performing gpg operations in the command line you will have to uncomment the line again for it to function in Tower as usual.

Use Another Computer

Keybase makes it really easy to manage your public key. To use gpg on a second computer, ensure GPGTools and Keybase is installed and then run these commands:

keybase pgp export | gpg --import

Now your public key is in your gpg keychain. Now you need to import the stubs from the smart card. Simply insert it and run:

gpg --card-status

This will generate the stubs required on the computer. You can now repeat the steps above to configure git commits on this second machine.

SSH with PIV

While technically it's certainly possible to authenticate SSH sessions using GPG, gpg-agent does not always play well with ssh-agent.

Using PIV for authenticating SSH remains the recommended solution. Only RSA keys are supported so we can't use ECC for authentication. The 4096 key size is not supported by PIV so we must use RSA2048. This should be sufficient for most cases.

Install yubico-piv-tool:

brew install yubico-piv-tool

Note that Homebrew must be installed first for this to work.

Generate a private key using the management key:

yubico-piv-tool -s 9a -a generate -k --pin-policy=once --touch-policy=never --algorithm=RSA2048 -o public.pem

Create a self-signed certificate. I could have made a certificate signing request to be signed by an internal CA but this should be sufficient. The only use for this self-signed certificate is to make the PIV/PKCS#11 lib happy:

yubico-piv-tool -a verify-pin -a selfsign-certificate -s 9a -S '/CN=ssh/' --valid-days=1000 -i public.pem -o cert.pem

Enter your normal Yubikey PIN.

Import the self-signed certificate:

yubico-piv-tool -k -a import-certificate -s 9a -i cert.pem

Enter the Management key

Install Opensc and save its location to a variable:

brew install opensc
export OPENSC_PK=$(brew --prefix opensc)/lib/opensc-pkcs11.so

Now export the file, and add a comment to better identify it:

ssh-keygen -D $OPENSC_PK -e > yubikey-ssh.pub
vi yubikey-ssh.pub

Note: the above command will export all keys stored on the Yubikey device. So if you have multiple keys you will need to edit the file to include only the public key that is associated with the private key we just generated.

Once in the editor, you can type A, followed by the comment, followed by Esc ZZ to exit and save.

View the random art for fun:

ssh-keygen -lv -f yubikey-ssh.pub

To check the slot status (optional):

yubico-piv-tool -a status

Move the public key to the authorized_keys file on the server

You could copy the file public key id to your clipboard in the terminal quite easily:

cat yubikey-ssh.pub | pbcopy

If you are moving it to an unRAID server do the following:

  • Append the public key file to /boot/config/ssh/root.pubkeys
  • Copy /etc/ssh/sshd_config to /boot/config/ssh
  • Modify /boot/config/sshd_config to set the following line:
AuthorizedKeysFile      /etc/ssh/%u.pubkeys

If you are using a more traditional Linux server, the file to append should be ~/.ssh/authorized_keys

Authenticate to the server with the new key

You can verify that you can log in to the server with the new key that exists on the smart card

ssh -I $OPENSC_PK user@remote.example.com

This is pretty awesome. Now let's setup ssh-agent so we don't need to keep typing the PIN every time we connect to a new server. The PIN will be cached by ssh-agent after the first PIN entry as long as the smart card remains connected to your system.

Make ssh-agent work in MacOS 10.12

I found the solution to this issue here:

rm /usr/local/lib/opensc-pkcs11.so
cp -a $OPENSC_PK /usr/local/lib/opensc-pkcs11.so
ssh-add -s /usr/local/lib/opensc-pkcs11.so

I would recommend setting that last command as an alias. This way, it is as simple as typing loadkey after restart or inserting the key. In my bash profile I have:

alias loadkey='ssh-add -s /usr/local/lib/opensc-pkcs11.so'
alias unloadkey='ssh-add -e /usr/local/lib/opensc-pkcs11.so'

You can now confirm that the correct public key has been added:

ssh-add -L

Now to log in to the server you simply type an ssh command as usual:

ssh user@remote.example.com

Brandon Kalinowski

I specialize in integrating technology seamlessly to help others tell compelling stories. For instance, I helped a professor construct a live television studio. I also managed a student news program. These and other experiences spurred a fascination with live streaming. I intern for Legion M as a streaming technical and data analyst. My expertise includes modern web design, video editing, and photography.


Share this post

Back To Top