Yubikey SSH authentication
The YubiKey is a hardware authentication device manufactured by Yubico that supports one-time passwords, public-key encryption and authentication, and the Universal 2nd Factor (U2F) and FIDO2 protocols developed by the FIDO Alliance.
It allows users to securely log into their accounts by emitting one-time passwords or using a FIDO-based public/private key pair generated by the device. YubiKey also allows for storing static passwords for use at sites that do not support one-time passwords.
Facebook uses YubiKey for employee credentials, and Google supports it for both employees and users. Some password managers support YubiKey. Yubico also manufactures the Security Key, a device similar to the YubiKey, but focused on public-key authentication.
Today I will show you how to prepare the Yubikey key to connect to the server.
Step 1: Install and configure software
- GNU/Linux: install gnupg, gpg-agent, and YubiKey NEO Manager.
- Modify ~/.gnupg/gpg.conf to set your preferences. An example configuration:
use-agent personal-cipher-preferences AES256 AES192 AES CAST5 personal-digest-preferences SHA512 SHA384 SHA256 SHA224 cert-digest-algo SHA512 default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
Step 2: Generate PGP Keys
Generate a new master key:
gpg --expert --full-generate-key gpg (GnuPG) 2.2.12; 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. gpg: keybox '/home/vasilij/.gnupg/pubring.kbx' created 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 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 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 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 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (3072) 4096 Requested keysize is 4096 bits Please specify how long the key should be valid. 0 = key does not expire = key expires in n days w = key expires in n weeks m = key expires in n months y = key expires in n years Key is valid for? (0) 2y Key expires at 2021 m. birželio 01 d. 10:06:08 EEST Is this correct? (y/N) y GnuPG needs to construct a user ID to identify your key. Real name: John Doe Email address: johndoe@localhost Comment: You selected this USER-ID: "John Doe <johndoe@localhost>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O 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: /home/vasilij/.gnupg/trustdb.gpg: trustdb created gpg: key 04D23D2F098D895F marked as ultimately trusted gpg: directory '/home/vasilij/.gnupg/openpgp-revocs.d' created gpg: revocation certificate stored as '/home/vasilij/.gnupg/openpgp-revocs.d/7848D7F318181F4AA62CD70104D23D2F098D895F.rev' public and secret key created and signed. pub rsa4096 2019-06-02 [C] [expires: 2021-06-01] 7848D7F318181F4AA62CD70104D23D2F098D895F uid John Doe <johndoe@localhost>
Create a text backup of the master key:
gpg -a --export-secret-keys 04D23D2F098D895F > masterkeys.txt
Generate the revocation certificate for the master key:
gpg --gen-revoke 04D23D2F098D895F > 04D23D2F098D895F-revoke-cert.asc sec rsa4096/04D23D2F098D895F 2019-06-02 John Doe <johndoe@localhost> Create a revocation certificate for this key? (y/N) y Please select the reason for the revocation: 0 = No reason specified 1 = Key has been compromised 2 = Key is superseded 3 = Key is no longer used Q = Cancel (Probably you want to select 1 here) Your decision? 3 Enter an optional description; end it with an empty line: > Reason for revocation: Key is no longer used (No description given) Is this okay? (y/N) y ASCII armored output forced. Revocation certificate created. Please move it to a medium which you can hide away; if Mallory gets access to this certificate he can use it to make your key unusable. It is smart to print this certificate and store it away, just in case your media become unreadable. But have some caution: The print system of your machine might store the data and make it available to others!
Generate the authentication subkey:
gpg --expert --edit-key 04D23D2F098D895F gpg (GnuPG) 2.2.12; 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. gpg: checking the trustdb gpg: marginals needed: 3 completes needed: 1 trust model: pgp gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u gpg: next trustdb check due at 2021-06-01 sec rsa4096/04D23D2F098D895F created: 2019-06-02 expires: 2021-06-01 usage: C trust: ultimate validity: ultimate [ultimate] (1). John Doe <johndoe@localhost> gpg> addkey 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 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? s Possible actions for a RSA key: Sign Encrypt Authenticate Current allowed actions: Encrypt (S) Toggle the sign capability (E) Toggle the encrypt capability (A) Toggle the authenticate capability (Q) Finished Your selection? e Possible actions for a RSA key: Sign Encrypt Authenticate Current allowed actions: (S) Toggle the sign capability (E) Toggle the encrypt capability (A) Toggle the authenticate capability (Q) Finished Your selection? a Possible actions for a RSA key: Sign Encrypt Authenticate Current allowed actions: Authenticate (S) Toggle the sign capability (E) Toggle the encrypt capability (A) Toggle the authenticate capability (Q) Finished Your selection? q RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (3072) 4096 Requested keysize is 4096 bits Please specify how long the key should be valid. 0 = key does not expire = key expires in n days w = key expires in n weeks m = key expires in n months y = key expires in n years Key is valid for? (0) 2y Key expires at 2021 m. birželio 01 d. 10:17:02 EEST Is this correct? (y/N) Really create? (y/N) y 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/04D23D2F098D895F created: 2019-06-02 expires: 2021-06-01 usage: C trust: ultimate validity: ultimate ssb rsa4096/8FB8B60D9095F628 created: 2019-06-02 expires: 2021-06-01 usage: A [ultimate] (1). John Doe <johndoe@localhost> gpg> save
Export your secret subkeys for backup.
gpg -a --export-secret-subkeys 04D23D2F098D895F > subkeys.txt
Step 3: Configure YubiKey
1. Make sure your YubiKey is plugged in and check if gpg can read it:
gpg --card-status Reader ...........: 1050:0407:X:0 Application ID ...: XXXXXXXX Version ..........: 2.1 Manufacturer .....: Yubico Serial number ....: XXXXXXXX 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 : 1 3 3 Signature counter : 0 Signature key ....: [none] Encryption key....: [none] Authentication key: [none] General key info..: [none]
2. Change the PIN and Admin PIN from its defaults (123456 and 12345678, respectively):
gpg --card-edit gpg/card> admin Admin commands are allowed gpg/card> passwd gpg: OpenPGP card no. XXXXXXXX detected 1 - change PIN 2 - unblock PIN 3 - change Admin PIN 4 - set the Reset Code Q - quit Your selection? 3 PIN changed. 1 - change PIN 2 - unblock PIN 3 - change Admin PIN 4 - set the Reset Code Q - quit Your selection? 1 Error changing the PIN: Bad PIN 1 - change PIN 2 - unblock PIN 3 - change Admin PIN 4 - set the Reset Code Q - quit Your selection? q gpg/card> quit
Step 4: Load PGP keys onto Yubikey
Move the authentication subkey to your YubiKey:
gpg --edit-key 04D23D2F098D895F gpg (GnuPG) 2.2.12; 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/04D23D2F098D895F created: 2019-06-02 expires: 2021-06-01 usage: C trust: ultimate validity: ultimate ssb rsa4096/8FB8B60D9095F628 created: 2019-06-02 expires: 2021-06-01 usage: A [ultimate] (1). John Doe <johndoe@localhost> gpg> toggle sec rsa4096/04D23D2F098D895F created: 2019-06-02 expires: 2021-06-01 usage: C trust: ultimate validity: ultimate ssb rsa4096/8FB8B60D9095F628 created: 2019-06-02 expires: 2021-06-01 usage: A [ultimate] (1). John Doe <johndoe@localhost> gpg> key 1 sec rsa4096/04D23D2F098D895F created: 2019-06-02 expires: 2021-06-01 usage: C trust: ultimate validity: ultimate ssb* rsa4096/8FB8B60D9095F628 created: 2019-06-02 expires: 2021-06-01 usage: A [ultimate] (1). John Doe <johndoe@localhost> gpg> keytocard Please select where to store the key: (3) Authentication key Your selection? 3 sec rsa4096/04D23D2F098D895F created: 2019-06-02 expires: 2021-06-01 usage: C trust: ultimate validity: ultimate ssb* rsa4096/8FB8B60D9095F628 created: 2019-06-02 expires: 2021-06-01 usage: A [ultimate] (1). John Doe <johndoe@localhost> gpg> save
Verify that the key is on the card:
gpg --card-status Reader ...........: 1050:0407:X:0 Application ID ...: XXXXXXXX Version ..........: 2.1 Manufacturer .....: Yubico Serial number ....: XXXXXXXX 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 ...: rsa4096 rsa2048 rsa4096 Max. PIN lengths .: 127 127 127 PIN retry counter : 0 3 3 Signature counter : 0 Signature key ....: 7848 D7F3 1818 1F4A A62C D701 04D2 3D2F 098D 895F created ....: 2019-06-02 07:06:18 Encryption key....: [none] Authentication key: D629 1EF3 DDEA 0BD8 C0E8 A911 8FB8 B60D 9095 F628 created ....: 2019-06-02 07:16:31 General key info..: pub rsa4096/04D23D2F098D895F 2019-06-02 John Doe <johndoe@localhost> sec> rsa4096/04D23D2F098D895F created: 2019-06-02 expires: 2021-06-01 card-no: 0006 06955733 ssb> rsa4096/8FB8B60D9095F628 created: 2019-06-02 expires: 2021-06-01 card-no: 0006 06955733
Once we move the master key from the local machine and onto offline storage, the master key will appear as sec# in the output of
gpg --card-status
The # means that the corresponding private key is not present.
We are now ready to use our YubiKey for SSH authentication.
Step 5: Configure gpg-agent and add your SSH keys
gpg-agent needs to be configured for SSH support. gpg-agent will take over the functionality of ssh-agent.
If you’ve created your GPG keys on a separate machine (e.g., A) you’ll need to make sure that the machine you’ll be using the Yubikey on (e.g., B) has a copy of the generated public key.
One way to do this is to upload your public key to a keyserver. Another way is to export the key as an ASCII file and import it manually.
On machine A:
gpg --armor --export 04D23D2F098D895F > 04D23D2F098D895F.asc
Then on machine B:
gpg --import < 04D23D2F098D895F.asc
You can also check if your YubiKey is working with ssh-add -L
ssh-rsa AAAAB3NzaC1yc2EAAAA... cardno:XXXXXXXX
Step 6: Test the SSH connection
We can now test an SSH connection to the remote machine. We will be asked for the PIN to unlock the key; if successful, we will be able to SSH successfully.
Use the -v verbose flag with ssh to see which key is being used:
ssh -v [email protected] OpenSSH_7.9p1 Ubuntu-10, OpenSSL 1.1.1b 26 Feb 2019 debug1: Reading configuration data /etc/ssh/ssh_config debug1: /etc/ssh/ssh_config line 19: Applying options for * debug1: Connecting to XXX.XXX.XXX.XXX [XXX.XXX.XXX.XXX] port 22. debug1: Connection established. debug1: Local version string SSH-2.0-OpenSSH_7.9p1 Ubuntu-10 debug1: Remote protocol version 2.0, remote software version OpenSSH_7.4 debug1: match: OpenSSH_7.4 pat OpenSSH_7.0*,OpenSSH_7.1*,OpenSSH_7.2*,OpenSSH_7.3*,OpenSSH_7.4*,OpenSSH_7.5*,OpenSSH_7.6*,OpenSSH_7.7* compat 0x04000002 debug1: Authenticating to XXX.XXX.XXX.XXX:22 as 'root' debug1: SSH2_MSG_KEXINIT sent debug1: SSH2_MSG_KEXINIT received debug1: kex: algorithm: curve25519-sha256 debug1: kex: host key algorithm: ecdsa-sha2-nistp256 debug1: kex: server->client cipher: [email protected] MAC:compression: none debug1: kex: client->server cipher: [email protected] MAC: compression: none debug1: expecting SSH2_MSG_KEX_ECDH_REPLY debug1: Server host key: ecdsa-sha2-nistp256 SHA256:0YB3eFixKKjO+0ybqG/JwpHUHYPGyDN1hIUu0SfqH6I debug1: Host 'XXX.XXX.XXX.XXX' is known and matches the ECDSA host key. debug1: rekey after 134217728 blocks debug1: SSH2_MSG_NEWKEYS sent debug1: expecting SSH2_MSG_NEWKEYS debug1: SSH2_MSG_NEWKEYS received debug1: rekey after 134217728 blocks debug1: Will attempt key: cardno:XXXXXXXXXXXX RSA SHA256:7M02qveHErgtrX4iwPJnaa0NyJrTmtByv1oTGZd7enU agent debug1: SSH2_MSG_EXT_INFO received debug1: kex_input_ext_info: server-sig-algs= debug1: SSH2_MSG_SERVICE_ACCEPT received debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password debug1: Next authentication method: gssapi-keyex debug1: No valid Key exchange context debug1: Next authentication method: gssapi-with-mic debug1: Unspecified GSS failure. Minor code may provide more information No Kerberos credentials available (default cache: FILE:/tmp/krb5cc_1000) debug1: Unspecified GSS failure. Minor code may provide more information No Kerberos credentials available (default cache: FILE:/tmp/krb5cc_1000) # this is where we see our YubiKey is being used debug1: Next authentication method: publickey debug1: Offering public key: cardno:XXXXXXXXXXXX RSA SHA256:7M02qveHErgtrX4iwPJnaa0NyJrTmtByv1oTGZd7enU agent debug1: Server accepts key: cardno:XXXXXXXXXXXX RSA SHA256:7M02qveHErgtrX4iwPJnaa0NyJrTmtByv1oTGZd7enU agent debug1: Authentication succeeded (publickey). Authenticated to XXX.XXX.XXX.XXX ([XXX.XXX.XXX.XXX]:22). debug1: channel 0: new [client-session] debug1: Requesting [email protected] debug1: Entering interactive session. debug1: pledge: network debug1: client_input_global_request: rtype [email protected] want_reply 0 debug1: Sending environment. debug1: Sending env LC_ADDRESS = lt_LT.UTF-8 debug1: Sending env LC_NAME = lt_LT.UTF-8 debug1: Sending env LC_MONETARY = lt_LT.UTF-8 debug1: Sending env LC_PAPER = lt_LT.UTF-8 debug1: Sending env LANG = en_US.UTF-8 debug1: Sending env LC_IDENTIFICATION = lt_LT.UTF-8 debug1: Sending env LC_TELEPHONE = lt_LT.UTF-8 debug1: Sending env LC_MEASUREMENT = lt_LT.UTF-8 debug1: Sending env LC_TIME = lt_LT.UTF-8 debug1: Sending env LC_NUMERIC = lt_LT.UTF-8