Skip to content

Instantly share code, notes, and snippets.

@jamesog
Last active January 24, 2024 23:17
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamesog/b156c7a85e7f95046ca8b95f6f857f70 to your computer and use it in GitHub Desktop.
Save jamesog/b156c7a85e7f95046ca8b95f6f857f70 to your computer and use it in GitHub Desktop.
YubiKey as an SSH CA

Edit 2023-03-03: This is now written in long-form at https://jamesog.net/2023/03/03/yubikey-as-an-ssh-certificate-authority/

The original version is retained below.


Prerequisites

  • ykman from the yubikey-manager package
  • libykcs11 from the yubico-piv-tool package

Steps

  1. Get a YubiKey to act as a dedicated CA - this key should not be used for other purposes
  2. Generate a certificate on the CA key
  3. Obtain the public key for the CA
  4. Collect SSH public keys to be signed
  5. Sign them!

Set up CA YubiKey

Identify the YubiKey

This is mostly necessary if you use multiple YubiKeys. You can skip this if you only have the CA key.

ykman list

Check the PIV info of the device you want to use

ykman --device <serial from above> piv info

(You can skip the --device flag if you only have one key.)

Setup the device

Assuming this is a brand new key, change the management key, PIN and PUK from the default values.

ykman --device <serial> piv change-management-key --touch --generate

Ref. https://developers.yubico.com/yubikey-piv-manager/PIN_and_Management_Key.html for PIN limitations

You can use pwgen to generate a secure PIN, e.g.

pwgen -sy 8 1

Change the PIN and PUK. 123456 / 12345678 are the default values, respectively.

ykman --device <serial> piv change-pin -P 123456
ykman --device <serial> piv change-puk -p 12345678

We only need CCID mode on the CA YubiKey, OTP and FIDO won't be used, so let's disable them.

% ykman --device <serial> mode CCID
Set mode of YubiKey to CCID? [y/N]: y

(N.B. I had to reinsert the YubiKey after doing this, otherwise ykman generated a traceback with error ykman.driver_ccid.CCIDError: Failed to transmit with protocol T1. Card was reset. during the next step.)

Generate CA

Create a private key. We'll use slot 9d.

Ref. https://developers.yubico.com/PIV/Introduction/Certificate_slots.html

% ykman --device <serial> piv generate-key 9d ca.pem
Enter a management key [blank to use default key]:
Touch your YubiKey...

And now generate a certificate whose subject is "SSH CA" and is valid for ~10 years. This outputs the public key to ca.pem.

% ykman --device <serial> piv generate-certificate -s "SSH CA" -d 3650 9d ca.pem
Enter PIN:
Enter a management key [blank to use default key]:
Touch your YubiKey...

At this point we have a private key and self-signed certificate on the YubiKey

% ykman --device <serial> piv info
PIV version: 5.2.6
PIN tries remaining: 3
CHUID:  ...
CCC:    ...
Slot 9d:
    Algorithm:  RSA2048
    Subject DN: CN=SSH CA
    Issuer DN:  CN=SSH CA
    Serial:     ...
    Fingerprint:    ...
    Not before: 2020-10-04 15:21:26
    Not after:  2030-10-02 15:21:26

Let's get the SSH pubkey for this. We'll need a PKCS#11 library, either libykcs (provided with yubico-piv-tool) or OpenSC.

ssh-keygen -D /opt/local/lib/libykcs11.dylib

(N.B. if you have multiple YubiKeys installed this will be problematic.)

This will output two ssh-rsa keys. Take the first one and put it in a file called ca.pub. This is used for signing.

Getting the public key from another YubiKey used for SSH

This section is if you are using a separate YubiKey as your SSH private key.

Get the SSH pubkey for your SSH YubiKey (NOT the CA) if you don't already have it.

N.B. unplug the CA key first, otherwise you'll get both.

% ssh-keygen -D /opt/local/lib/libykcs11.dylib > yk5.pub

Signing an SSH public key

If you use a second YubiKey for SSH (as in the previous section), unplug that key now and ensure only the CA key is plugged in.

Sign your SSH key's pubkey. The key identity (-I) will be your email address and we add a principle (-n) of your SSH username.

% ssh-keygen -s ca.pub -D /opt/local/lib/libykcs11.dylib -I bob@example.com -n bob yk5.pub
Enter PIN for 'YubiKey PIV #xxxxx':
Signed user key yk5-cert.pub: id "bob" serial 0 valid forever

You can inspect this with

% ssh-keygen -f yk5-cert.pub -L
yk5-cert.pub:
        Type: ssh-rsa-cert-v01@openssh.com user certificate
        Public key: RSA-CERT SHA256:Xk7dUop5/qjwucmsUduwTtG9hgBmOE/jD3UJh+wqlVY
        Signing CA: RSA SHA256:TCVSthFvCaEuONOxziMLwJpdJAoY19fb4eZADw0Ovtw (using rsa-sha2-512)
        Key ID: "bob"
        Serial: 0
        Valid: forever
        Principals:
                bob
        Critical Options: (none)
        Extensions:
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc
@YoNevelt
Copy link

YoNevelt commented Feb 6, 2023

Hi,
I'm following your tutorial on a rapsberry pi (not Mac as the /opt/local/lib/libykcs11.dylib path and filename suggests) and for me the yubico-piv-tool package doesn't install libykcs11 at all. I had to it manually from the official source.
So I modified the path to generate an SSH public key:

ssh-keygen -D /usr/lib/arm-linux-gnueabihf/libykcs11.so

But then I get an error message:

cannot read public key from pkcs11

Therefore I can't continue the tutorial. Any suggestions?

@jamesog
Copy link
Author

jamesog commented Feb 6, 2023

Yeah, this guide was written as some pretty rough notes and it's definitely incomplete. I posted it more for my own reference really, I didn't intend for others to use it :-)

I probably have some more complete notes somewhere. I'll see if I can dig them out sometime.

@jamesog
Copy link
Author

jamesog commented Mar 3, 2023

Hey @YoNevelt, I finally got around to writing this up properly: https://jamesog.net/2023/03/03/yubikey-as-an-ssh-certificate-authority/

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