# NB: This document describles a 'Old-School' way of using Yubikey with SSH Modern OpenSSH has native support for FIDO Authentication. Its much simpler and should also be more stable with less moving parts. OpenSSH also now has support for signing arbitary files witch can be used as replacement of gnupg. Git also supports signing commits/tags with ssh keys. ### Pros of FIDO * Simpler stack / less moving parts * Works directly with `ssh`, `ssh-add` and `ssh-keygen` on most computers * Simpler * Private key can never leave the FIDO device ### Cons of FIDO * Server needs to support `ecdsa-sk` or `ed25519-sk` signatures * Can't import your existing SSH keys * Private key can never leave the FIDO device for backup purposes. * By default `ssh-keygen` generates a FIDO key that is tied to single computer (key handle is a file in `.ssh/`) * Some FIDO devices do not support `resident` keys where key handle is saved in the FIDO device itself (makes it harder to use same key on multible computers) * My [NitroKey Start](https://shop.nitrokey.com/shop/nksa-nitrokey-start-6) with [Gnuk firmware](https://www.fsij.org/category/gnuk.html) does not support FIDO :( ### Pros of GnuPG * Supports older algorithms that are more widely supported on legacy systems like RSA and normal ed25519 * Can import existing SSH keys * Possible to make backups of your keys to multiple OpenPGP cards/yubikeys/print on paper * E-mail signing and encryption * Fun for hardcore nerds ### Cons of GnuPG * Very complex * Bad UX * Hard to understand documentation * Breaks easily (wrong ENV variables, pcsc conflicts) * Fun for only hardcore nerds... # OpenPGP SSH access with Yubikey and GnuPG Yubikey, Smart Cards, OpenSC and GnuPG are pain in the ass to get working. Those snippets here sould help alleviate pain. Notes written here should work on * Ubuntu 22.04 with Gnome * Debian 12 with Gnome * Linux Mint with Cinnamon (needs different environment setup, check comments) * Arch Linux with Gnome (pacman instead of apt) **This is not a step by step guide** Depending on your environment, some commands might change and some parts can be skipped. **I also recommend reading [The Linux Foundation - Protecting code integrity with PGP](https://github.com/lfit/itpol/blob/master/protecting-code-integrity.md) document. It gives a really good overview of basic GnuPG and OpenPGP consepts** # Yubikey Config To reset and disable not used modes on Yubikey you need the `ykman` program You can install it using this command ```sh sudo apt install yubikey-manager ``` GnuPG usage only needs CCID mode to be enabled. FIDO mode can also be enabled for [WebAuthn](https://en.wikipedia.org/wiki/WebAuthn) ```sh # Only enable chip card interface device ykman config mode FIDO+CCID # Older ykman (< v4.0) uses different syntax ykman mode FIDO+CCID ``` Yubikey OpenPGP applet that is used by GnuPG can be configured with ```sh ykman openpgp ``` # GnuPG environment setup for Ubuntu/Debian and Gnome desktop Make sure that gnupg, pcscd and scdaemon are installed ```sh sudo apt install gnupg pcscd scdaemon ``` GnuPG Smart Card stack looks something like this ```text Yubikey -> pcscd -> scdaemon -> gpg-agent -> gpg commandline tool and other clients ``` Now we have to tell scdaemon to use pcsc interface instead of the default direct connect mode. ```sh mkdir ~/.gnupg cat > ~/.gnupg/scdaemon.conf <<'EOF' disable-ccid pcsc-driver /usr/lib/x86_64-linux-gnu/libpcsclite.so.1 card-timeout 1 # Always try to use yubikey as the first reader # even when other smart card readers are connected # Name of the reader can be found using the pcsc_scan command # If you have problems with gpg not recognizing the Yubikey # then make sure that the string here matches exacly pcsc_scan # command output. Also check journalctl -f for errors. reader-port Yubico YubiKey EOF ``` > Under Ubuntu *libpcsclite.so* is in package called *libpcsclite1*. > `dpkg -L libpcsclite1` command can show the location of the lib. ### GnuPG Trust Model Turn on ssh like trust on first use (tofu) ```sh cat > ~/.gnupg/gpg.conf <<'EOF' trust-model tofu+pgp EOF ``` After changing gpg configuration files, it's a good idea to restart gpg-agent. ```sh systemctl --user restart gpg-agent.service ``` # Testing that gpg sees your Yubikey If everything went well then running following command should show something like this ```sh gpg --card-status ``` ```sh Reader ...........: Yubico Yubikey 4 CCID 00 00 Application ID ...: D2760001240102010006054860180000 Version ..........: 2.1 Manufacturer .....: Yubico Serial number ....: 05486018 Name of cardholder: [not set] Language prefs ...: [not set] Sex ..............: unspecified URL of public key : [not set] Login data .......: [not set] Signature PIN ....: not forced Key attributes ...: rsa2048 rsa2048 rsa2048 Max. PIN lengths .: 127 127 127 PIN retry counter : 3 0 3 Signature counter : 0 Signature key ....: [none] Encryption key....: [none] Authentication key: [none] General key info..: [none] ``` ## Debugging ### Test if Yubikey is detected `pcsc-tools` package contains `pcsc_scan` program that can be used to check that Yubikey is detected. ```sh sudo apt install pcsc-tools ``` and then run ```sh pcsc_scan ``` Now you should see Card inserted and removed events on your terminal when connecting and removing Yubikey. ### Restart everything Smart Card middleware ```sh sudo systemctl restart pcscd.service ``` gpg-agent ```sh systemctl --user restart gpg-agent.service ``` ### Check the logs Run journalctl in another terminal window and look for scdaemon log lines ```sh journalctl -fan100 ``` If you see sharing violation messages then something else is probably trying to use the yubikey via opensc. Check [getting-estonian-id-card-and-gnupg-scdaemon-yubikey-work-together](#getting-estonian-id-card-and-gnupg-scdaemon-yubikey-work-together) ## Switch from OpenSSH ssh-agent to GnuPG as ssh-agent ### Temporarily First get you need to get GnuPG agent-ssh-socket path ```sh gpgconf --list-dirs | grep ssh ``` That should return something like this ```sh agent-ssh-socket:/run/user/1000/gnupg/S.gpg-agent.ssh ``` And then you can set that path as SSH_AUTH_SOCK environment variable ```sh export SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh ``` After that `ssh-add -l` shoud show your Yubikey. ### Permanent **This only works with [Gnome Display Manager (GDM)](https://wiki.archlinux.org/title/GDM) under systemd** KDE, Cinnamon and other desktops that use different login program might need a different config. Check the comments for more info. ```sh #!/bin/sh echo "Create required directories" mkdir ~/.config/autostart mkdir ~/.config/environment.d echo "==> Disable Gnome-Keyring ssh component" cp /etc/xdg/autostart/gnome-keyring-ssh.desktop ~/.config/autostart echo "Hidden=true" >> ~/.config/autostart/gnome-keyring-ssh.desktop echo "==> Point ssh agent socket environment variable to GnuPG" cat > ~/.config/environment.d/99-gpg-agent_ssh.conf <<'EOF' SSH_AUTH_SOCK=${XDG_RUNTIME_DIR}/gnupg/S.gpg-agent.ssh EOF echo "==> Done" echo echo "Restart you computer and then GnuPG will be your ssh-agent" echo ``` ## Getting Estonian ID card and GnuPG scdaemon Yubikey work together. [Estonian ID-card](https://e-estonia.com/solutions/e-identity/id-card/) uses [OpenSC](https://github.com/OpenSC/OpenSC/wiki) project to access private keys on the smart card. OpenSC also supports Yubikey and that will create conflicts with GnuPG scdaemon. To fix it you can just disable Yubikey in opensc. ```sh #!/bin/sh echo "Create required directories" mkdir ~/.config/environment.d mkdir ~/.config/opensc echo "==> Creating user local OpenSC configuration" cat > ~/.config/environment.d/99-opensc.conf <<'EOF' OPENSC_CONF=${HOME}/.config/opensc/config EOF cat > ~/.config/opensc/config <<'EOF' app default { # debug = 3; # debug_file = opensc-debug.txt; # Lenovo USB Smartcard Keyboard pinpad implementation is broken reader_driver pcsc { enable_pinpad = false } # Only GnuPG uses Yubikey ignored_readers = "Yubico YubiKey" framework pkcs15 { # use_file_caching = true; } # Force Yubikey to use openpgp applet card_atr 3B:F8:13:00:00:81:31:FE:15:59:75:62:69:6B:65:79:34:D4 { name = "Yubico Yubikey"; driver = "openpgp"; } } EOF echo "==> Done" echo echo "Restart your computer and Yubikey support in OpenSC will be disabled" echo ``` To make coperation between OpenSC and scdaemon even better then you have to configure scdaemon to use shared access mode, Arch Linux wiki has a short paragraph about that here https://wiki.archlinux.org/index.php/GnuPG#Shared_access_with_pcscd # GnuPG commands `gpg -k` or `gpg --list-keys` - List stored public keys `gpg -K` or `gpg --list-private-keys` - List all stored private keys, `#` means private key is unavailable, `>` means private key is on a smartcard # Generate OpenPGP keys with GnuPG 1. Generate 2048bit RSA master key with Certify(Master) and Sign permissions, expire key after 2 years ``` gpg --quick-generate-key "Full Name " rsa2048 cert,sign 2y ``` 2. Add a 2048bit RSA encryption subkey that expires after 2 years ``` gpg --quick-add-key master_key_fingerprint rsa2048 encrypt 2y ``` where `master_key_fingerprint` is a 40 char hex string shown when running `gpg -K` # Converting openssh private key format to pem man page says that you can use `-e` option to convert private and public keys to other formats, that seems to be wrong. Instead you can use `-p` option to request changing the password but not actually setting the password. cp ~/.ssh/id_rsa /tmp/id_rsa # Make a copy of the ssh private key ssh-keygen -p -f /tmp/id_rsa -m pem # Converting pem to OpenPGP [Monkeysphere](http://web.monkeysphere.info/) project includes a `pem2openpgp` command that can be used to import ssh private keys to gnupg keyring. sudo apt install libcrypt-openssl-bignum-perl libcrypt-openssl-rsa-perl curl https://raw.githubusercontent.com/dkg/monkeysphere/master/src/share/keytrans > /tmp/pem2openpgp chmod +x /tmp/pem2openpgp /tmp/pem2openpgp 'ssh id_rsa' < /tmp/id_rsa | gpg --import The imported key is stored without encryption, add it with those commands: gpg --edit-key and then use passwd command and type the same password as your master key passwd After importing you can use normal `gpg --edit-key` command to change parameters on this key. GnuPG 2.1 also allows you to move the imported key to be one of your subkeys for authentication. https://security.stackexchange.com/a/160847 Maybe also works * https://superuser.com/questions/1414381/how-to-import-an-ssh-ed25519-key-to-gpg # Move imported SSH key to a subkey of another master key ## Move the key 1. Get the imported key keygrip value `gpg --with-keygrip -k` 2. `gpg --expert --edit-key ` where `master_key_id` is a 40 char hex string shown when running `gpg -K` 3. type `addkey` 4. select `(13) Existing key` 5. Copy and Paste imported ssh key keygrip 6. Toggle off all capabilities and enable authenticate capability and finish 7. Set key valid time to 2 years with `2y` 8. Confirm key creation and type your master key password 9. Type `save` to save and exit from edit menu ## Delete old public ssh key This key is no longer needed ```sh gpg --expert --delete-keys ``` where `ssh_key_id` is a 40 char hex string shown when running `gpg -K` # Export private keys Before moving private keys to yubikey you must make a backup of private keys so that when you lose or break your yubikey you could move the same keys to a new yubikey. ``` gpg --export-secret-keys > secret_keys.asc ``` Exported keys are encrypted with your master password. Its also a good idea to print your private keys on a paper because files can bitrot and become unusable after some time. ``` paperkey < secret_keys.asc > secret_paperkey.txt ``` https://wiki.archlinux.org/index.php/Paperkey # Move your private keys to Yubikey gpg --edit-key and then use `keytocard` command to move the primary key to card. Then select first sub key with `key 1` and then move that to card with `keytocard`. Then unselect first key with command `key` and then select second subkey with `key 2` and then do `keytocard`. After that `save` and you are done. # Cloning OpenPGP card ID with [Gnuk/Nitrokey Start](https://github.com/Nitrokey/nitrokey-start-firmware/blob/bf92ae69f5728139b411c3438e06c2e00e821560/src/openpgp-do.c#L637) Edit `openpgpcard_aid` array in `src/openpgp-do.c` to contain your target card ID and then compile and flash the resulting firmware. ## Alternative option for same key on different OpenPGP cards You can tell scdaemon to force learn the new card ID with this command gpg-connect-agent "scd serialno" "learn --force" /bye Source: https://github.com/drduh/YubiKey-Guide#switching-between-two-or-more-yubikeys # Other Notes scdaemon with shared access for ubuntu 18.04 https://d.arti.ee/scdaemon_2.2.4-1ubuntu1.2_amd64.deb * https://gist.github.com/artizirk/71d7ae140c58f38e45d84d34f7fcc341 What do ssb and other mean in gpg --list-keys output ```txt sec => 'SECret key' ssb => 'Secret SuBkey' pub => 'PUBlic key' sub => 'public SUBkey' ``` `#` after sec/ssb means that secret key is unavailable, maybe it was exported and then deleted `>` after sec/ssb means that secret key is on a smartcard/yubikey # Windows * https://www.dionysopoulos.me/windows-10-gpg-ssh-and-github.html * https://justyn.io/blog/using-a-yubikey-for-gpg-in-wsl-windows-subsystem-for-linux-on-windows-10/ * https://github.com/benpye/wsl-ssh-pageant * Pipe everything to everywhere in windows https://github.com/rupor-github/win-gpg-agent ![win-gpg-agent](https://raw.githubusercontent.com/rupor-github/win-gpg-agent/a2222b194db8d800e89b07a17d7be510f9152d11/docs/pic1.png)